• 回溯总结


    回溯

    1. 组合问题

    1. 组合问题 每次for都是从startIndex开始

    2. 每个元素 用一次和用多次体现在 backtrack(i+1)用一次还是backtrack(i)用多次

    3. 组合问题不需要used数组,去重也不需要used数组那个判断

    77. n元素下的k元素组合

    labuladong 题解思路

    给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

    你可以按 任何顺序 返回答案。

    示例 1:

     输入:n = 4, k = 2
     输出:
     [
      [2,4],
      [3,4],
      [2,3],
      [1,2],
      [1,3],
      [1,4],
     ]

    示例 2:

     输入:n = 1, k = 1
     输出:[[1]]
    思路
    1. 按回溯模板 直接写

    2. 注意剪枝操作, 超过k个元素不再进入递归 24ms->8ms

    代码
     class Solution {
     public:
         int n;
         int k;
         vector<int> path;
         vector<vector<int>> all;
         vector<vector<int>> combine(int n, int k) {
             this->n = n;
             this->k = k;
             path.clear();
             all.clear();
             backtrack(1);
             return all;
        }
         void backtrack(int num){
             if(path.size() == k){
                 all.push_back(path);
                 return;
            }
      //进行剪枝, 超过k个元素不再进入递归 24ms->8ms
             for(int i = num; i<=n - (k-path.size()) + 1; i++){
                 path.push_back(i);
                 backtrack(i+1);
                 path.pop_back();
            }
        }
     };

    39. 组合总和

    labuladong 题解思路

    给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

    candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

    对于给定的输入,保证和为 target 的不同组合数少于 150 个。

    示例 1:

     输入:candidates = [2,3,6,7], target = 7
     输出:[[2,2,3],[7]]
     解释:
     2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
     7 也是一个候选, 7 = 7 。
     仅有这两种组合。

    示例 2:

     输入: candidates = [2,3,5], target = 8
     输出: [[2,2,2,2],[2,3,3],[3,5]]
    思路
    1. 注意这个题 单个元素是可以重复使用的 表现在代码上 for循环内 backtrack是i 而不是i+1

    2. sum 和 target 的判断逻辑注意下

    代码
     class Solution {
     public:
         vector<vector<int>> ans;
         vector<int> path;
     
         void backtracking(vector<int>& candidates, int index, int target, int sum){
           //这里sum和target的判断逻辑注意下
           if(sum>target) return;
           if(sum == target) {
             ans.push_back(path);
             return;
          }
           for(int i = index; i<candidates.size(); i++){
             path.push_back(candidates[i]);
             backtracking(candidates, i, target, sum + candidates[i]); //注意 这里的i不加1 不然没有数重复的
             path.pop_back();
          }
        }
       
         vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
           backtracking(candidates,0, target, 0);
           return ans;
        }
     };

    216. 组合总和 III

    labuladong 题解

    找出所有相加之和为 nk 个数的组合,且满足下列条件:

    • 只使用数字1到9

    • 每个数字 最多使用一次

    返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

    示例 1:

     输入: k = 3, n = 7
     输出: [[1,2,4]]
     解释:
     1 + 2 + 4 = 7
     没有其他符合的组合了。

    示例 2:

     输入: k = 3, n = 9
     输出: [[1,2,6], [1,3,5], [2,3,4]]
     解释:
     1 + 2 + 6 = 9
     1 + 3 + 5 = 9
     2 + 3 + 4 = 9
     没有其他符合的组合了。
    思路
    1. 结束终止满足两个条件之一 nowSum >= n || path.size() == k即可

    2. 满足条件的结果 nowSum == n && path.size() == k

    代码
     class Solution {
     public:
         vector<vector<int>> all;
         vector<int> path;
         int k,n ;
         vector<vector<int>> combinationSum3(int k, int n) {
             this->k = k;
             this->n = n;
             backtrack(1, 0);
             return all;
        }
     
         void backtrack(int cur, int nowSum){
             //if(cur>9) return; //注意这句不能要 不然会 9 45判错 最后cur 10 直接返回掉
             if(nowSum >= n || path.size() == k){
                 if(nowSum == n && path.size() == k)
                     all.push_back(path);
                 return;
            }
     
             for(int i = cur; i<=9; i++){
                 path.push_back(i);
                 backtrack(i+1, nowSum + i);
                 path.pop_back();
            }
        }
     };

    40. 组合总和 II

    labuladong 题解

    给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

    candidates 中的每个数字在每个组合中只能使用 一次

    注意:解集不能包含重复的组合。

     

    示例 1:

     输入: candidates = [10,1,2,7,6,1,5], target = 8,
     输出:
     [
     [1,1,6],
     [1,2,5],
     [1,7],
     [2,6]
     ]

    示例 2:

     输入: candidates = [2,5,2,1,2], target = 5,
     输出:
     [
     [1,2,2],
     [5]
     ]
    思路
    1. 结束终止满足两个条件之一 nowSum >= n || path.size() == k即可

    2. 满足条件的结果 nowSum == n && path.size() == k

    代码
     class Solution {
     public:
         //去重操作 数组中包含重复元素 所以要去重
         //注意 并不需要used数组 used数组只在排列中用到?即(for(int i = 0)的时候用到)
         vector<vector<int>> all;
         vector<int> path;
         vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
             //排序让相同的元素挨着
             sort(candidates.begin(), candidates.end());
             backtrack(candidates, target, 0, 0);
             return all;
        }
     
         void backtrack(vector<int>& candidates, int target, int curIndex, int nowSum){
             if(nowSum>target) return;
             if(nowSum == target){
                 all.push_back(path);
                 return;
            }
             for(int i = curIndex; i<candidates.size(); i++){
                 // 经典的去重操作 全排列中也用到了
                 if(i>curIndex && candidates[i] == candidates[i-1])
                     continue;
                 path.push_back(candidates[i]);
                 //这里是i+1,每个数字在每个组合中只能使用一次
                 backtrack(candidates, target, i+1,  nowSum+candidates[i]);
                 path.pop_back();
            }
        }
     };

     

    377. 组合总和 Ⅳ

    给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

    题目数据保证答案符合 32 位整数范围。

     

    示例 1:

     输入:nums = [1,2,3], target = 4
     输出:7
     解释:
     所有可能的组合为:
     (1, 1, 1, 1)
     (1, 1, 2)
     (1, 2, 1)
     (1, 3)
     (2, 1, 1)
     (2, 2)
     (3, 1)
     请注意,顺序不同的序列被视作不同的组合。

    示例 2:

     输入:nums = [9], target = 3
     输出:0
    思路
    1. 名为组合 实为排列 解法dp

    2. 回溯超时 dp解答

    3. 记忆化dfs 其实就是dp了吧

    暴力回溯代码
     class Solution {
     public:
         int ans;
         int combinationSum4(vector<int>& nums, int target) {
             ans = 0;
             backtrack(nums, 0, target);
             return ans;
        }
     
         void backtrack(vector<int>& nums, int nowSum, int target){
             if(nowSum>target) return;
             if(nowSum == target){
                 ans++;
                 return;
            }
          //可以反向 就是排列了
             for(int i = 0; i<nums.size(); i++){
                 backtrack(nums, nowSum+nums[i], target);
            }
        }
     };
    记忆化dfs
     class Solution {
     public:
         int combinationSum4(vector<int>& nums, int target) {
             return dfs(nums, target);
        }
         //备忘录,保存每层递归的计算结果,用于实现记忆化。
         unordered_map<int, int> memo;
         //dfs(target)的定义: 用nums中的元素凑成总和为target(每个元素可以使用多次),用多少中凑法。
         int dfs(vector<int>& nums, int target){
             if(target == 0)
                 return 1;
             if(target < 0)
                 return 0;
             if(memo.count(target) == 1)
                 return memo[target];
             int res = 0;
             for(int i = 0; i < nums.size(); i++){
                 res += dfs(nums, target - nums[i]);
            }
             memo[target] = res;
             return res;
        }
     };
    dp代码
     class Solution {
     public:
         int combinationSum4(vector<int>& nums, int target) {
             //使用dp数组,dp[i]代表组合数为i时使用nums中的数能组成的组合数的个数
             //dp[i]=dp[i-nums[0]]+dp[i-nums[1]]+dp[i=nums[2]]+...
             //举个例子比如nums=[1,3,4],target=7;
             //dp[7]=dp[6]+dp[4]+dp[3]
             //其实就是说7的组合数可以由三部分组成,1和dp[6],3和dp[4],4和dp[3];
             vector<unsigned long long> dp(target+1);
             //是为了算上自己的情况,比如dp[1]可以由dp【0】和1这个数的这种情况组成。
             dp[0] = 1;
             for(int i = 1; i<=target; i++){
                 for(int num : nums){
                     //dp用int的话 有一个很傻逼的越界,需要 && dp[i - num] < INT_MAX - dp[i]
                     if(i>=num)  
                         dp[i] += dp[i-num];
                }
            }
             return dp[target];
        }
     };

     

  • 相关阅读:
    「学习笔记」Min25筛
    hash索引
    Thread的setDaemon(true)方法的作用
    Reactor模式详解
    题目整理
    jstat命令查看jvm的GC情况 (以Linux为例)
    jvm参数调优
    以网游服务端的网络接入层设计为例,理解实时通信的技术挑战
    Java 工程师成神之路
    ID生成 雪花算法
  • 原文地址:https://www.cnblogs.com/qianxunslimg/p/16009980.html
Copyright © 2020-2023  润新知