给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
update 10:00
思路:
此题目是典型的动态规划思路,将复杂的问题拆解成若子问题,如此题目:
首先对condidates从小到大排序
循环开始
设要解决的是 func(candidates[i:],target)
循环过程
可以分解为 condidates[i] + func(candidates[i:],target-condidates[i]) | func(condidates[i+1:],target)
循环终止条件
i==len(condidates)-1 直接判断 target是否可以整除
i< len(condidates) 如果condidate[i] > target 终止查找,返回未找到,
效率优化
通常动态规划都可以使用空间来换时间,如这里可以保存 func(candidates[i:],target) 找到的序列,如果有其他的子问题也到这里,可以直接返回,不用再次进行分解:
按照此思路有代码:
class Solution {
public:
vector<vector<int>> getCombin(vector<int>& c, int target,int index) {
// printf("\r\n %d %d\r\n",target,index);
vector<vector<int>> ret;
if (target < c[index])
return ret;
if (c.size() == index+1 || target == c[index]){
if (target % c[index] == 0){
vector<int> tmp;
for(int i = 0 ; i < target / c[index] ;i++){
tmp.push_back(c[index]);
}
ret.push_back(tmp);
}
return ret;
} else{
vector<vector<int>> net_result = getCombin(c,target-c[index],index);
for(int i=0;i<net_result.size();i++){
net_result[i].insert(net_result[i].begin(),c[index]);
}
vector<vector<int>> net_result2 = getCombin(c,target,index+1);
// printf("\r\n ----%d %d\r\n",target,index);
for (int i =0 ;i<net_result2.size();i++){
net_result.push_back(net_result2[i]);
}
return net_result;
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
vector<vector<int>> ret;
if (candidates.size() == 0)
return ret;
return getCombin(candidates,target,0);
}
};
这里图简单,没有把空间换时间加上
现在我来吧空间换时间加上试试:
class Solution {
public:
map<pair<int,int>,vector<vector<int> > > tmp_result;
vector<vector<int>> getCombin(vector<int>& c, int target,int index) {
if (tmp_result.find({target,index}) != tmp_result.end()) {
return tmp_result[{target,index}];
}
vector<vector<int>> ret;
if (target < c[index]){
tmp_result[{target,index}] = ret;
return ret;
}
if (c.size() == index+1 || target == c[index]){
if (target % c[index] == 0){
vector<int> tmp;
for(int i = 0 ; i < target / c[index] ;i++){
tmp.push_back(c[index]);
}
ret.push_back(tmp);
}
tmp_result[{target,index}] = ret;
return ret;
} else{
vector<vector<int>> net_result = getCombin(c,target-c[index],index);
for(int i=0;i<net_result.size();i++){
net_result[i].insert(net_result[i].begin(),c[index]);
}
vector<vector<int>> net_result2 = getCombin(c,target,index+1);
// printf("\r\n ----%d %d\r\n",target,index);
for (int i =0 ;i<net_result2.size();i++){
net_result.push_back(net_result2[i]);
}
tmp_result[{target,index}] = net_result;
return net_result;
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
vector<vector<int>> ret;
if (candidates.size() == 0)
return ret;
return getCombin(candidates,target,0);
}
};
提升不是很明显.
考虑到在递归调用返回时有大量的数组拷贝传递,可以进一步优化(这个是我看leedcode中其他人的评论得到的)
class Solution {
public:
vector<vector<int>> vt;
vector<int> vx;
void getsome(vector<int>& vis, int index, int cnt){
if(cnt == 0){
vt.push_back(vx);
return ;
}
if(index >= vis.size() || cnt < 0)
return ;
if(vis[index] <= cnt){
vx.push_back(vis[index]);
getsome(vis, index, cnt-vis[index]);
vx.pop_back();
}
getsome(vis, index+1, cnt);
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
if(candidates.empty()){
return vt;
}
getsome(candidates, 0, target);
return vt;
}
};
- 相比于我之前写的代码在返回参数时减少了一次vector的拷贝,所以节约了时间,这种做法最终的时间是
但这种做法递归间复用vx ,vt的全局变量, 无法写成多线程,而我之前的做法可以改成多线程从而加快速度.