代码随想录算法训练营第30天|回溯复习篇

回溯基础理论

1.回溯的本质是利用递归进行暴力搜索,将符和条件的结果集搜索出来

2.回溯法常见的问题:

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 棋盘问题:N皇后,解数独等等

3.回溯法常见模板

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

4.回溯法抽象为一个图形来理解就容易多了在后面的每一道回溯法的题目我都将遍历过程抽象为树形结构,利用树形结构可以形象化回溯的过程,便于理解,所以在解决回溯问题时,可以将二叉搜索树画出来

复习题1

77. 组合 - 力扣(LeetCode)

二叉搜索你树

回溯三部曲:

1.确认递归返回值及参数

    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

res用于收集最后所有可能的结果,list用于存储符合条件的单一结果

2.回溯终止条件

当list的大小为k时既符合题目条件

 if (list.size() == k) {
            res.add(new ArrayList<>(list));
            return;
        }

3.单层搜索的过程、

从startindex开始进行遍历,将符合条件的数加入到list中后继续向下递归

for循环每次从startIndex开始遍历,然后用list保存取到的节点i。

  for (int i = start; i <= n - (k - list.size()) + 1; i++) {
            list.add(i);
            dfs(k, n, i + 1);
            list.remove(list.size() - 1);
        }

 完整代码:

class Solution {

    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

    public void dfs(int k, int n, int start) {
        if (list.size() == k) {
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = start; i <= n - (k - list.size()) + 1; i++) {
            list.add(i);
            dfs(k, n, i + 1);
            list.remove(list.size() - 1);
        }
        return;
    }

    public List<List<Integer>> combine(int n, int k) {
        dfs(k,n,1);
        return res;
    }
}

复习题2

216. 组合总和 III - 力扣(LeetCode)

二叉搜索树

 本体与上题的搜索过程是像相同的,在条件上加了一个和为指定数

回溯三部曲

1.确认递归返回值及参数

    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

res用于收集最后所有可能的结果,list用于存储符合条件的单一结果

2.回溯终止条件

当list的大小为k,并且和为n时既符合题目条件,或者当前list中元素的和已经大于n时也没有继续搜索的必要了

      if(sum>n){
            return;
        }
        if (list.size() == k&&sum==n) {
            res.add(new ArrayList<>(list));
            return;
        }

3.单层搜索过程

从startindex开始进行遍历,将符合条件的数加入到list中后,sum对其相加后 继续向下递归,回溯时将list最后的数移除,sum进行相减

for循环每次从startIndex开始遍历,然后用list保存取到的节点i。

 for (int i = start; i <= 9; i++) {
            sum += i;// 处理
            list.add(i);

            dfs(n, k, i + 1, sum);// 向下搜

            sum -= i;// 回溯
            list.remove(list.size() - 1);
        }

完整代码:

class Solution {
    public  List<List<Integer>> res = new ArrayList<>();

    public  List<Integer> list = new ArrayList<>();

    public  void dfs(int n, int k, int start, int sum) {

        if (sum > n) {
            return;// (如果sum>n那么后面在加就没有意义了)剪枝
        }
        if (list.size() == k && sum == n) {
            res.add(new ArrayList<>(list));// 收集结果
            return;
        }
        for (int i = start; i <= 9; i++) {
            sum += i;// 处理
            list.add(i);

            dfs(n, k, i + 1, sum);// 向下搜

            sum -= i;// 回溯
            list.remove(list.size() - 1);
        }
        return;
    }

    public List<List<Integer>> combinationSum3(int k, int n) {
        int sum = 0;
        dfs(n, k, 1, sum);
        return res;
    }
}

 复习题3

216. 组合总和 III - 力扣(LeetCode)

二叉搜索树

 本题的不同点在于数组中的元素可以多次使用,所以在下次递归时的startIndex不用加一还是从当前元素开始

回溯三部曲

1.确认递归返回值及参数

    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

res用于收集最后所有可能的结果,list用于存储符合条件的单一结果

2.递归终止条件

当list的元素的和为targetSum时既符合题目条件或者当sum大于targetSum时也没有继续搜索的必要了

  //sum>=target就返回
        if (sum > target) {
            return;
        }
        if (sum == target) {
            res.add(new ArrayList<>(list));
            return;
        }

3.单层搜索过程
从startIndex开始对nums数组进行遍历将nums数组的元素加入到list中后进行向下递归,注意下次的开始位置任然为当前元素位置(本题的元素可以重复选)

  for (int i = start; i < nums.length; i++) {
            list.add(nums[i]);//处理
            sum += nums[i];

            dfs(nums, target, i);//递归,注意这里是i(表示可以重复读取当前的数)

            list.remove(list.size() - 1);//回溯
            sum -= nums[i];
        }

完整代码:

class Solution {
    public  List<List<Integer>> res = new ArrayList<>();
    public  List<Integer> list = new ArrayList<>();
    public  int sum = 0;

     /**
     * @param nums    目标数组
     * @param target  目标和
     * @param start   起始位置
     */
    public  void dfs(int[] nums, int target, int start) {
   
        //sum>=target就返回
        if (sum > target) {
            return;
        }
        if (sum == target) {
            res.add(new ArrayList<>(list));
            return;
        }

        for (int i = start; i < nums.length; i++) {
            list.add(nums[i]);//处理
            sum += nums[i];

            dfs(nums, target, i);//递归,注意这里是i(表示可以重复读取当前的数)

            list.remove(list.size() - 1);//回溯
            sum -= nums[i];
        }
    }

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        dfs(candidates, target, 0);
        return res;

    }
}

复习题4

40. 组合总和 II - 力扣(LeetCode)

二叉搜索树

 本题较上题元素不能重复使用,并且数组中存在重复元素需要考虑去重的问题(利用set将选过的数进行保存,在遍历前判断是否存在来避免重复选择相同的元素,使用前提需要对数组进行排序,为了将相同元素放到一起)

回溯三步曲:

1.确认递归返回值及参数

    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

res用于收集最后所有可能的结果,list用于存储符合条件的单一结果

2.递归终止条件

当list的元素的和为targetSum时既符合题目条件或者当sum大于targetSum时也没有继续搜索的必要了

  //sum>=target就返回
        if (sum > target) {
            return;
        }
        if (sum == target) {
            res.add(new ArrayList<>(list));
            return;
        }

3.单层搜索过程从startIndex进行搜索,将数组中的数加入,后向下递归回溯

  HashSet<Integer> set = new HashSet<>();
        for (int i = startIndex; i <nums.length ; i++) {
            if(set.contains(nums[i])){
                continue;
            }
            set.add(nums[i]);
            list.add(nums[i]);
            sum+=nums[i];

            dfs(nums,targetSum,sum,i+1);

            sum-=nums[i];
            list.remove(list.size()-1);
        }

 完整代码:

public static List<List<Integer>> res = new ArrayList<>();
    public static List<Integer> list = new ArrayList<>();

    /**

     */

    public static void dfs(int[]nums,int targetSum,int sum,int startIndex) {
         if(sum>targetSum){
                return;
          }
            if(sum==targetSum){
               res.add(new ArrayList<>(list));
               return;
            }
        HashSet<Integer> set = new HashSet<>();
        for (int i = startIndex; i <nums.length ; i++) {
            if(set.contains(nums[i])){
                continue;
            }
            set.add(nums[i]);
            list.add(nums[i]);
            sum+=nums[i];

            dfs(nums,targetSum,sum,i+1);

            sum-=nums[i];
            list.remove(list.size()-1);
        }
    }

    public static List<List<Integer>> f(int []nums, int targetSum) {
       Arrays.sort(nums);
       dfs(nums,targetSum,0,0);
       return res;
    }

复习题5

多个集合求组合

17. 电话号码的字母组合 - 力扣(LeetCode)

数字和字母如何映射

   利用map将数字和字符进行映射  


    public void f(String digits) {
        map.put(2, "abc");
        map.put(3, "def");
        map.put(4, "ghi");
        map.put(5, "jkl");
        map.put(6, "mno");
        map.put(7, "pqrs");
        map.put(8, "tuv");
        map.put(9, "wxyz");
    }

 回溯三部曲

1.确认参数和返回值

res用于存储最终答案,ans用于存储单一搜索答案

 public List<String> res = new ArrayList<>();

 public StringBuilder ans = new StringBuilder();

利用len表示当前res的长度,key表示当前遍历的数字

2.递归终止条件

如题可知当res的长度等于digits时递归终止

 if(len==digits.length()){//收集结果
            res.add(ans.toString());
            return;
        }
        

3.单一搜索过程

对digits进行遍历后对向下一个数字对应的字符进行递归

 
        String str=map.get(digits.charAt(key)-'0');//获取当前需要操作的字符串,如“23”,即先2,3
        
        for (int i =0; i <str.length() ; i++) {
              ans.append(str.charAt(i));//处理

              dfs(digits,map,len+1,key+1);//向下搜

              ans.delete(ans.length()-1,ans.length());//回溯

        }

完整代码:

class Solution {
    public HashMap<Integer, String> map = new HashMap<>();

    public List<String> res = new ArrayList<>();

    public StringBuilder ans = new StringBuilder();

    public void f(String digits) {
        map.put(2, "abc");
        map.put(3, "def");
        map.put(4, "ghi");
        map.put(5, "jkl");
        map.put(6, "mno");
        map.put(7, "pqrs");
        map.put(8, "tuv");
        map.put(9, "wxyz");
    }

     /**
     * 
     * @param digits  目标参数
     * @param map     映射map
     * @param num     当前操作的字符串在map中对应的key,
     * @param len     当前字符ans的长度
     */
    public  void dfs(String digits,HashMap<Integer,String> map,int len,int key ){
        f(digits);
        if(len==digits.length()){//收集结果
            res.add(ans.toString());
            return;
        }
        
        String str=map.get(digits.charAt(key)-'0');//获取当前需要操作的字符串,如“23”,即先2,3
        
        for (int i =0; i <str.length() ; i++) {
              ans.append(str.charAt(i));//处理

              dfs(digits,map,len+1,key+1);//向下搜

              ans.delete(ans.length()-1,ans.length());//回溯

        }
    }

    public List<String> letterCombinations(String digits) {
        if(digits==null||digits.length()==0){
            return res;
        }
        dfs(digits, map, 0,0);
        return res;
    }
}

复习题6

切割问题

131. 分割回文串 - 力扣(LeetCode)

 切割问题依然可以抽象成组合问题

递归用来纵向遍历,for循环用来横向遍历,切割线(就是图中的红线)切割到字符串的结尾位置,说明找到了一个切割方法。

判断回文

 public  boolean cheak(String s,int start,int end){
        for (int i = start,j=end; i <j ; i++,j--) {
            if(s.charAt(i)!=s.charAt(j)){
                return false;
            }
        }
        return true;
    }

 本题注意 利用startIndex代表分割线当startIndex大于了是s.length()也就到达了叶子节点、

完整代码:

class Solution {
       public List<List<String>>  res=new ArrayList<>();

    public  List<String> list=new ArrayList<>();

    public  boolean cheak(String s,int start,int end){
        for (int i = start,j=end; i <j ; i++,j--) {
            if(s.charAt(i)!=s.charAt(j)){
                return false;
            }
        }
        return true;
    }
    public  void dfs(String s,int startIndex) {
        if (startIndex >= s.length()) {
            res.add(new ArrayList(list));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            //如果是回文子串,则记录
            if (cheak(s, startIndex, i)) {
                String str = s.substring(startIndex, i + 1);
                list.add(str);
            } else {
                continue;
            }
            //起始位置后移,保证不重复
            dfs(s, i + 1);
            list.remove(list.size() - 1);
        }
    }
    public List<List<String>> partition(String s) {
        dfs(s, 0);
        return res;
    }
}

复习题7

子集问题

78. 子集 - 力扣(LeetCode)

子集问题与组合问题不同的是对所有的叶子节点都需要搜集,所以在搜集答案时不需要return 了

 

本题其实可以不需要加终止条件,因为startIndex >= nums.size(),本层for循环本来也结束了,本来我们就要遍历整棵树。

有的同学可能担心不写终止条件会不会无限递归?

并不会,因为每次递归的下一层就是从i+1开始的。

如果要写终止条件,注意:result.push_back(path);要放在终止条件的上面,如下:

result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉结果
if (startIndex >= nums.size()) { // 终止条件可以不加
    return;
}

 完整代码:

class Solution {
    public  List<List<Integer>> res=new ArrayList<>();

    public  List<Integer> list=new ArrayList<>();

    public  void dfs(int []nums,int startIndex){
        res.add(new ArrayList<>(list));
        for (int i = startIndex; i <nums.length ; i++) {
            list.add(nums[i]);
            dfs(nums,i+1);
            list.remove(list.size()-1);
        }
    }
    public List<List<Integer>> subsets(int[] nums) {
       dfs(nums,0);
       return res;
    }
}

在回溯算法:求子集问题(二) (opens new window)中,开始针对子集问题进行去重。

class Solution {
    public  List<List<Integer>> res=new ArrayList<>();

    public  List<Integer> list=new ArrayList<>();

    public  void dfs(int []nums,int startIndex){
        res.add(new ArrayList<>(list));
        for (int i = startIndex; i <nums.length ; i++) {
            if(i>startIndex&&nums[i]==nums[i-1]){
                continue;
            }
            list.add(nums[i]);
            dfs(nums,i+1);
            list.remove(list.size()-1);
        }
    }
    public List<List<Integer>> subsetsWithDup(int[] nums) {
          Arrays.sort(nums);
        dfs(nums,0);
        return res;
    }
}

复习题8

排列问题

46. 全排列 - 力扣(LeetCode)

排列问题与祝贺问题不同的是排列问题与与顺序有关如[1,2]和[2,1]是相同的组合但是是不同的排列

那么可能前面选了2后面可能选1 也就说明每次都从0开始,然后需要used数组对已经选过的数进行保存

完整代码:

class Solution {
    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();
    public boolean[] isUse = new boolean[10];

    /**
     * @param nums 数组
     * @param x    当前放数的位置
     */
    public  void dfs(int[] nums, int x) {
        if (x == nums.length) {
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (!isUse[i]) {

                isUse[i] = true;
                list.add(nums[i]);

                dfs(nums, x + 1);

                isUse[i] = false;
                list.remove(list.size() - 1);
            }
        }
    }

    public List<List<Integer>> permute(int[] nums) {
dfs(nums, 0);
        return res;
    }
}

47. 全排列 II - 力扣(LeetCode)

在此题基础上增加去重过程即可

class Solution {
      public  List<List<Integer>> res = new ArrayList<>();
    public  List<Integer> list = new ArrayList<>();
    public  boolean[] isUse = new boolean[10];

    /**
     * @param nums 数组
     * @param x    当前放数的位置
     */
    public void dfs(int[] nums, int x) {
        if (x == nums.length) {
            res.add(new ArrayList<>(list));
            return;
        }
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if(set.contains(nums[i])){//对同层选过的节点进行去重
                continue;
            }
            if (!isUse[i]) {

                isUse[i] = true;
                list.add(nums[i]);
                set.add(nums[i]);

                dfs(nums, x + 1);

                isUse[i] = false;
                list.remove(list.size() - 1);
            }
        }
    }
    public List<List<Integer>> permuteUnique(int[] nums) {
        dfs(nums, 0);
        return res;
    }
}

关于去重

去重问题

以上我都是统一使用used数组来去重的,其实使用set也可以用来去重!

在本周小结!(回溯算法系列三)续集 (opens new window)中给出了子集、组合、排列问题使用set来去重的解法以及具体代码,并纠正一些同学的常见错误写法。

同时详细分析了 使用used数组去重 和 使用set去重 两种写法的性能差异:

使用set去重的版本相对于used数组的版本效率都要低很多,大家在leetcode上提交,能明显发现。

原因在回溯算法:递增子序列 (opens new window)中也分析过,主要是因为程序运行的时候对unordered_set 频繁的insert,unordered_set需要做哈希映射(也就是把key通过hash function映射为唯一的哈希值)相对费时间,而且insert的时候其底层的符号表也要做相应的扩充,也是费时的。

而使用used数组在时间复杂度上几乎没有额外负担!

使用set去重,不仅时间复杂度高了,空间复杂度也高了,在本周小结!(回溯算法系列三) (opens new window)中分析过,组合,子集,排列问题的空间复杂度都是O(n),但如果使用set去重,空间复杂度就变成了O(n^2),因为每一层递归都有一个set集合,系统栈空间是n,每一个空间都有set集合。

那有同学可能疑惑 用used数组也是占用O(n)的空间啊?

used数组可是全局变量,每层与每层之间公用一个used数组,所以空间复杂度是O(n + n),最终空间复杂度还是O(n)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/688092.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

fastadmin/thinkPHPQueue消息队列详细教程

thinkphp-queue 是thinkphp 官方提供的一个消息队列服务,它支持消息队列的一些基本特性: 消息的发布,获取,执行,删除,重发,失败处理,延迟执行,超时控制等队列的多队列, 内存限制 ,启动,停止,守护等消息队列可降级为同步执行1、通过composer安装thinkPHP消息队列 …

kafka-消费者-指定offset消费(SpringBoot整合Kafka)

文章目录 1、指定offset消费1.1、创建消费者监听器‘1.2、application.yml配置1.3、使用 Java代码 创建 主题 my_topic1 并建立3个分区并给每个分区建立3个副本1.4、创建生产者发送消息1.4.1、分区0中的数据 1.5、创建SpringBoot启动类1.6、屏蔽 kafka debug 日志 logback.xml1…

算法003:快乐数

这道题采用快慢双指针的方法。 为了弄清楚这个题到底是要我们干嘛&#xff0c;我们把整个过程类比一下&#xff1a; 不管是n19还是n2&#xff0c;我们都把它当成一种判断链表是否有环的方式。 对于n19&#xff0c;题干是这样解释的&#xff1a; 我们把它当成链表&#xff0c…

如何挑选最适合你的渲染工具

随着技术的发展&#xff0c;云渲染平台逐渐成为设计师、动画师、影视制作人员等创意工作者的得力助手。然而&#xff0c;市场上的云渲染平台种类繁多&#xff0c;如何选择最适合自己的渲染工具成为了一个需要认真考虑的问题。 在挑选适合自己的云渲染工具时&#xff0c;我们需…

YOLOv10涨点改进:原创自研 | GhostNet融合 | 从廉价的操作中生成更多的特征图

文章目录 GhostNet理论基础实验部分改进方案新增yolov10s-ghost.yaml文件代码运行GhostNet理论基础 Ghost Module是一种模型压缩的方法,即在保证网络精度的同时减少网络参数和计算量,从而提升计算速度(speed),降低延时(latency)。Ghost 模块可以代替现有卷积网络中的每…

OpenAI模型规范概览

这是OpenAI对外分享的模型规范文档&#xff08;Model Spec&#xff09;&#xff0c;它定义了OpenAI希望在API接口和ChatGPT&#xff08;含GPT系列产品&#xff09;中模型的行为方式&#xff0c;这也是OpenAI超级对齐团队奉行的行为准则&#xff0c;希望能对国内做RLHF的同学有帮…

.Net实现SCrypt Hash加密

方案1 &#xff08;加密后存储“算法设置”、“盐(随机值)”、“Hash值”&#xff0c;以“$”分隔&#xff09;&#xff1a; //Nuget引入SCrypt.NET库 using Org.BouncyCastle.Crypto.Generators; using Scrypt; using System; using System.Security.Cryptography; namespace …

Apache OFBiz 路径遍历导致RCE漏洞复现(CVE-2024-36104)

0x01 产品简介 Apache OFBiz是一个电子商务平台,用于构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式电子商务类应用系统。是美国阿帕奇(Apache)基金会的一套企业资源计划(ERP)系统。该系统提供了一整套基于Java的Web应用程序组件和工具。 0x02 漏洞概…

90后机器人创业者再获10亿元融资,为精密传动行业注入新动力!

据了解&#xff0c;一位90后机器人创业者再次获得近10亿元人民币的融资&#xff0c;这一消息在精密传动行业引起了广泛关注。 杭州宇树科技有限公司&#xff08;简称“宇树”&#xff09;&#xff0c;2024年春节前完成了B2轮融资&#xff0c;融资近10亿元人民币&#xff0c;本轮…

pyqt5 tablewidget实现excel拖曳填充

代码主要涉及鼠标事件和绘图&#xff0c;selectionModel&#xff0c;selectedIndexes。 import sys from PyQt5.QtCore import QPoint, Qt, QCoreApplication, pyqtSlot from PyQt5.QtGui import QBrush, QPixmap, QColor, QPainter,QIcon,QPolygon from PyQt5.QtWidgets imp…

【Java】解决Java报错:ClassCastException

文章目录 引言1. 错误详解2. 常见的出错场景2.1 错误的类型转换2.2 泛型集合中的类型转换2.3 自定义类和接口转换 3. 解决方案3.1 使用 instanceof 检查类型3.2 使用泛型3.3 避免不必要的类型转换 4. 预防措施4.1 使用泛型和注解4.2 编写防御性代码4.3 使用注解和检查工具 5. 示…

64. UE5 RPG 创建新的双手攻击怪物

在上一篇文章中&#xff0c;我们实现了新的功能&#xff0c;现在可以创建多个普通攻击动画&#xff0c;并且可以根据你所使用的普通攻击动画&#xff0c;设置不同的攻击位置。比如&#xff0c;你使用武器&#xff0c;那么攻击位置需要从武器上获取&#xff0c;如果你没有持有武…

《精通ChatGPT:从入门到大师的Prompt指南》第2章:Prompt的基本概念

2.1 什么是Prompt 在了解和使用ChatGPT的过程中&#xff0c;理解和掌握Prompt的概念是至关重要的。Prompt可以简单地定义为一段指令或请求&#xff0c;它向AI模型提供了生成特定回应或行为所需的初始信息。具体而言&#xff0c;Prompt是用户与AI系统之间的桥梁&#xff0c;通过…

MatrixOne→MatrixOS:矩阵起源的创业史即将用“AI Infra”和“AI Platform”书写新章程

在数字化浪潮的推动下&#xff0c;MatrixOne的故事就像一部科技界的创业史诗&#xff0c;它始于一个简单而宏伟的梦想——构建一个能够支撑起新一代数字世界的操作系统。想象一下&#xff0c;在AIGC时代&#xff0c;数据流动如同“血液”&#xff0c;算法运转如同“心跳”&…

【Neo4j】Windows11使用Neo4j导入CSV数据可视化知识图谱

Windows11使用Neo4j导入CSV数据可视化知识图谱 序1. 安装JDK21&#xff08;1&#xff09;下载&#xff08;2&#xff09;安装&#xff08;3&#xff09;环境配置 2. 安装Neo4j&#xff08;1&#xff09;下载&#xff08;2&#xff09;解压安装&#xff08;3&#xff09;环境配置…

国货美妆品牌站上C位,抖音618大促期间相关产品销量同比增长53%

每年618大促时期是各类美妆产品销售旺季。近两年&#xff0c;爱美人士的囤货清单里出现越来越多国货美妆品牌。 据《2023年中国化妆品年鉴》&#xff0c;去年国内美妆市场总体规模达7972亿元&#xff0c;其中&#xff0c;国货市场份额达到50.4%&#xff0c;首次超越外资品牌。…

Cloudpods 强大的多云管理平台部署

简介 Cloudpods 是一款简单、可靠的企业IaaS资源管理软件。帮助未云化企业全面云化IDC物理资源&#xff0c;提升企业IT管理效率。 Cloudpods 帮助客户在一个地方管理所有云计算资源。统一管理异构IT基础设施资源&#xff0c;极大简化多云架构复杂度和难度&#xff0c;帮助企业…

遗址博物馆ar互动展示软件提供丰富的趣味化体验

在自然博物馆的每一个角落&#xff0c;都隐藏着大自然的奥秘与魅力。为了让每一位参观者都能深入体验、探索这些奥秘&#xff0c;我们引入了前沿的AR技术&#xff0c;为您带来一场前所未有的沉浸式自然之旅。 步入博物馆&#xff0c;您手中的AR相机将成为您的更佳向导。自然博物…

「动态规划」如何求最小路径和?

64. 最小路径和https://leetcode.cn/problems/minimum-path-sum/description/ 给定一个包含非负整数的m x n网格grid&#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。说明&#xff1a;每次只能向下或者向右移动一步。 输入&#xff1a;…

短视频矩阵源码----如何做正规开发规则分享:

一、什么是SaaS化服务技术开发&#xff1f; &#xff08;短视频矩阵系统是源头开发的应该分为3个端口---- 总后台控制端、总代理端口&#xff0c;总商户后台&#xff09; SaaS是软件即服务&#xff08;Software as a Service&#xff09;的缩写。它是一种通过互联网提供软件应…