LeetCode 39. Combination Sum【DFS+剪枝】⭐⭐⭐⭐⭐

题目描述

给一个不含重复元素数组和一个数target,求在可重复抽取数组元素的情况下,所有和为target的情况,输出要从小到大排序。

知识点

回溯DFS+剪枝

运行结果

在这里插入图片描述

码前思考

  1. 我一开始想要用动态规划中的完全背包+充满背包做的,但是很久没刷题了,想不起来该怎么做了。。。
  2. 我是看了网上的题解才懂得。
  3. 就是简单地暴力dfs,初始sum为0,然后暴力枚举每个数字,为了不含重复的情况,所以遍历后的数字就不能在后面考虑了。
  4. 同时,可以进行一个小小的剪枝,对数组进行排序,如果当前数字+sum超过了target,那么后面的数字也肯定超过target,所以直接放弃。

代码实现

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;

    //dfs函数表示以当前idx为基准的范围内是否能够得到结果
    void dfs(int idx,int sum,vector<int>& candidates, int target){
        if(sum == target){
            res.push_back(path);
            return;
        }
        for(int i=idx;i<candidates.size()&&sum+candidates[i]<=target;i++){
            path.push_back(candidates[i]);
            sum+=candidates[i];
            dfs(i,sum,candidates,target);
            path.pop_back();
            sum-=candidates[i];
        }
    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        //首先进行排序,方便剪枝
        sort(candidates.begin(),candidates.end());
        //使用加法进行dfs
        dfs(0,0,candidates,target);
        return res;
    }
};

//下面这份代码没有将判断超过target放到循环条件中,所以会很长,而且也没有剥离等于target的情况。。。dfs函数一般在一开始都会有递归边界的,这是需要注意的。

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;

    //dfs函数表示以当前idx为基准的范围内是否能够得到结果
    void dfs(int idx,int sum,vector<int>& candidates, int target){
        for(int i=idx;i<candidates.size();i++){
            path.push_back(candidates[i]);
            sum+=candidates[i];
            if(sum==target){
                res.push_back(path);
                path.pop_back();
                sum-=candidates[i];
            }else if(sum<target){
                dfs(i,sum,candidates,target);
                path.pop_back();
                sum-=candidates[i];
            }else{
                path.pop_back();
                sum-=candidates[i];
                break;//直接break,后面的肯定不行的~
            }
        }
    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        //首先进行排序,方便剪枝
        sort(candidates.begin(),candidates.end());
        //使用加法进行dfs
        dfs(0,0,candidates,target);
        return res;
    }
};

码后反思

  1. 太久没做题了,今天的手感异常生疏,感觉回到了解放前了。。。
  2. 突然夏令营没有机试了,会改成手撕代码吗,害,这太难了。。。感觉leetcode题目更加适合面试题,pat上的题太长了,所以刷刷leetcode热题100吧。。。
  3. 关于动态规划背包的解法,可以参考如下:
    类似于动态规划求满足条件的组合数的转移方程dp[i]+=dp[i-x]
    这里的转移方程为path[i]拼接上path[i-x]的所有再加上x的组合路径
    class Solution {
    public:
        vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
            vector<vector<vector<int>>> path(target+1);
            path[0]={{}};
            for(auto x : candidates){
                for(int i=x;i<=target;++i){
                    for(auto v : path[i-x]){
                        v.push_back(x);
                        path[i].push_back(v);
                    }
                }
            }
            return path[target];
        }
    };
    
    在这里插入图片描述
    算法执行的结果不太好。
    算法的思想就是跟背包差不多,表示的是最后选择的是第n件物品的情况下,有哪些可能情况,有点绕,不管了。。。掌握回溯就行
  4. dfs还是难呀,根本不知道会不会超时,反正dfs一定是剪枝的,这是毋庸置疑的。。。
  5. 既然没有机试了,那么就每天刷刷leetcode,或者看看微信公众号,保持思考吧。。。

参考题解

  1. 回溯算法 + 剪枝
  2. 图解算法微信公众号

二刷代码

看了提示,知道要用回溯来解题,所以得出了下面的代码:

//为了得到不重复的结果,需要强制只能使用自己后面的元素,一种典型的dfs暴力方法
//相似的题目还有之前那个求最短的
class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        int len = candidates.size();
        dfs(candidates,target,0);
        return res;    
    }

    void dfs(vector<int>& candidates,int target,int idx){
        if(target==0){//不需要求和了
            res.push_back(path);
        }else if(target<0){
            return;//小于0肯定不行
        }else{
            for(int i=idx;i<candidates.size();i++){
                path.push_back(candidates[i]);
                dfs(candidates,target-candidates[i],i);
                path.pop_back();
            }
        }
    }

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值