Combination Sum求和为给定值的所有序列的解题思路

来源:互联网 发布:淘宝电商是什么 编辑:程序博客网 时间:2024/05/18 17:40

因为数字可以重复,毫无疑问,这道题要用回溯来解。写出代码不难如下:

class Solution {public:    vector<vector<int> > rec_combinationSum(vector<int> &candidates, int target) {        vector<vector<int>> res;        if(target==0){        res.push_back(vector<int>());        return res;        }                int tar = target;        for(auto c:candidates){            target -=c;            if(target<0){target +=c;continue;}            auto vv = rec_combinationSum(candidates,target);            for(auto vec:vv){                if(accumulate(vec.begin(),vec.end(),c) == tar) {                    vec.push_back(c);                    sort(vec.begin(),vec.end());                    res.push_back(vec);                }            }            target +=c;        }        return res;    }    vector<vector<int> > combinationSum(vector<int> &candidates, int target){    vector<vector<int>> res;    if(candidates.size()==0) return res;    sort(candidates.begin(),candidates.end());    res = rec_combinationSum(candidates,target);    sort(res.begin(),res.end());        auto it=unique(res.begin(),res.end());        res.resize(distance(res.begin(),it));            return res;    }};

但是,有一个问题就是运行速度很慢,在Leetcode168个测试用例上出现了TLE,这是回溯需要解决的问题。如果先不考虑剪枝,是不是因为代码中大量的排序导致变慢呢?每次计算得到一个结果都要排序,而且还有不少重复的。如果用set或者unordered_set来存储是否可以搞定呢?要为unordered_set<vector<int>>写一个hash函数类,因为vector<int>不是基本基本类型,代码如下:

class MyHash{public:    size_t operator()(const vector<int> &v) const    {        size_t x = 0;        for (auto &i : v)            x ^= std::hash<int>()(i);        return x;    }};class Solution {public:    unordered_set<vector<int>,MyHash > rec_combinationSum(vector<int> &candidates, int target) {        unordered_set<vector<int>,MyHash> res;        if(target==0){        res.insert(vector<int>());        return res;        }                int tar = target;        for(auto c:candidates){            target -=c;            if(target<0){target +=c;continue;}            auto vv = rec_combinationSum(candidates,target);            for(auto vec:vv){                if(accumulate(vec.begin(),vec.end(),c) == tar) {                    vec.push_back(c);                    sort(vec.begin(),vec.end());                    res.insert(vec);                }            }            target +=c;        }        return res;    }    vector<vector<int> > combinationSum(vector<int> &candidates, int target){    unordered_set<vector<int>,MyHash> tmp;    vector<vector<int> > res;    if(candidates.size()==0) return res;    sort(candidates.begin(),candidates.end());    tmp = rec_combinationSum(candidates,target);        for(auto vec:tmp)    res.push_back(vec);            return res;    }};

结果发现仍然是TLE,这里虽然省去了排序,去重,理论上插入还是线性,但仍然不行。看来数据结构上的优化无法帮助解决本TLE问题。
下面只能考虑剪枝了。怎么剪?这个问题有什么特点?
正常情况下,当选定一个数字之后,下面仍然要把每个数字都再考虑一遍;有没有一种方法,某个数字的情况考虑完之后就不再考虑该数字出现的情况?存在不存在?答案是存在,那就是要对数列进行排序,排序之后,我们发现如果某个数字考虑过,则包含该数字的所有情况,包括重复的情况都在回溯的过程中考虑了。所以之后,就不必再考虑该种情况了,此处可剪枝。

class Solution {public:    vector<vector<int>> rec_combinationSum(vector<int> &candidates, int target,int idx) {        vector<vector<int>> res;        if(target==0){        res.push_back(vector<int>());        return res;        }                int tar = target;        for(int i=idx;i<candidates.size();++i){            target -=candidates[i];            if(target<0){target +=candidates[i];continue;}            auto vv = rec_combinationSum(candidates,target,i);            for(auto vec:vv){                if(accumulate(vec.begin(),vec.end(),candidates[i]) == tar) {                    vec.push_back(candidates[i]);                    sort(vec.begin(),vec.end());                    res.push_back(vec);                }            }            target +=candidates[i];        }        return res;    }    vector<vector<int> > combinationSum(vector<int> &candidates, int target){    vector<vector<int> > res;    if(candidates.size()==0) return res;    sort(candidates.begin(),candidates.end());    res = rec_combinationSum(candidates,target,0);            return res;    }};

增加了一个参数,该参数保存当前访问的数值,考虑完之后不再考虑之前的所有元素而是从下一个开始,达到了剪枝的目的。速度大概是108ms,仍然不佳。


0 0
原创粉丝点击