day 24 回溯 组合问题

在这里插入图片描述

组合问题:不讲究顺序,按照一定规则从N个中找出k个数
模板

//组合问题模板
    vector<vector<int>>result;
    vector<int>path;
    vector<vector<int>>main(vector<int>&nums) {
    	sort(nums.begin(), nums.end())
        dfs();
        return result;
    }
    void dfs(){
        if(终止条件){
            r
            return;
        }
        for(int i=start; i<nums.size(); i++){ //循环动作空间
        	剪枝() break;
            path.push_back(i);
            dfs();
            path.pop_back();
        }
    }
	无序组合类型总结 
	(1) 数组元素不重复  不可重复取  start = i+1
	(2) 数组元素不重复 可重复取 start = i
	(3) 数组元素重复  不可重复取   start = i+1  同一树层剪枝
	     if( i>start && candidates[i]==candidates[i-1]) continue;

216. 组合总和 III
1.动作范围 1-9 取k个 和为n
2. 组合问题不重复 i = start +1

 void dfs(int k,  int target, int start){
     
        if(target == 0 && path.size() == k){
            result.push_back(path);
            return;
        }

        for(int i=start; i<=9; i++){
            if(target - i <0 || path.size()==k) break;
            path.push_back(i);
            dfs(k, target-i, i+1);
            path.pop_back();
        }
    }

39. 组合总和
4. 组合 无序 为了保证不重复 使用start来表示起始位置
5. 结束条件 sum==target
6. 剪枝 sum > target
7. 无限制重复选取 start为 i 也就是从当前元素开始取

class Solution {
public:
    vector<vector<int>>result;
    vector<int>path;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        dfs(candidates, target, 0);
        return result;
    }
    void dfs(vector<int>& candidates, int target, int start){
        cout<<start << " " << candidates[start] <<" "<<target<<endl;
        if(target < 0) return;
        if(target == 0){
            result.push_back(path);
            return;
        }


        for(int i=start; i<candidates.size(); i++){
            path.push_back(candidates[i]);
            dfs(candidates, target-candidates[i], i);
            path.pop_back();
        }
    }
};

40. 组合总和 II
1.组合问题,用start保证顺序
2. 集合中每个元素只用选用一次 start = i+1;
3. 集合中有重复元素, 去重

(1) if( i>0 && candidates[i]==candidates[i-1]) continue;
这样会导致树枝层面的去重 (1,1,2)这样的结果丢失

(2) if( i>0 && candidates[i]==candidates[i-1] && uesed[i-1] == false) continue;
定义一个used数组来记录元素使用情况, 如果当前元素跟跟前一个元素相同 前一个元素没有用,这个肯定是重复的。因为所有的组合情况前面那个元素已经做了

(3) if( i>start && candidates[i]==candidates[i-1]) continue;
for循环就是同一个树层的动作选择 ,跳过同一树层使用过的元素,

for(int i=start; i<candidates.size(); i++){
            if( i>start && candidates[i]==candidates[i-1]) continue;
            path.push_back(candidates[i]);
            dfs(candidates, target-candidates[i], i+1);
            path.pop_back();
        }

377. 组合总和 Ⅳ

  1. 顺序不同的序列被视作不同的组合。去掉start for循环从下标0开始
  2. 重复计算太多 递归剪枝(记忆化搜索),否则会超时
 int dfs(vector<int>& candidates, int target){

        if(mp.find(target)!=mp.end()){return mp[target];}
          if(target == 0){
            mp[target] = 1;// 当taget=0时 方法个数为1
            return 1;
        }
        int cur = 0;
        for(int i=0; i<candidates.size(); i++){
            if(target - candidates[i] < 0) break;  //放在外面 需要遍历整个数组嘛
            //break比较省事
            cur += dfs(candidates, target-candidates[i]);
        
        }
        mp[target] = cur; //存在即返回 不存在就算完之后赋值
        return mp[target];
    }

17. 电话号码的字母组合
for循环是循环该树层的动作空间

class Solution {
public:
    unordered_map<char,string> mp={{'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"},  {'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}};
    vector<string>result;
    string path;
    vector<string> letterCombinations(string digits) {
        if(digits.size()==0) return {};
        dfs(digits, 0);
        return result;
    }

    void dfs(string digits, int index){
        if(index > digits.size()) return;
        if(index == digits.size()){
            result.push_back(path);
            return;
        }
        string ss = mp[digits[index]];
        for(int i=0; i<ss.size(); i++){  //每一个数字代表的动作空间
            path.push_back(ss[i]);
            dfs(digits, index+1);  //回溯下一个数字
            path.pop_back();
        }

    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值