39. 组合总和
给定一个无重复元素的数组 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] ]
其中:1 <= candidates[i] <= 200
40.组合总和II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。 解集不能包含重复的组合。
示例 1: 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
示例 2: 输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [ [1,2,2], [5] ]
个人总结:
1.candidates中数字可以无限制选取,index每次向下发散都从下标0开始,不可以被无限制选取则当前层i+1
2.集合不要求有序(1,1,2)和(2,1,1)是一个集合
39参考代码(测试用例2,3,5)
#include<iostream>
#include<vector>
#include<stdlib.h>
using namespace std;
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int> candidates,int target,int sum,int index){
if(sum >target)
return;
if(sum == target){
result.push_back(path);
return;
}
for(int i=index;i<candidates.size();i++){
path.push_back(candidates[i]);
sum+=candidates[i];
backtracking(candidates,target,sum,i);//因为可以无限次使用,所以i不进行加1操作
sum-=candidates[i];//回溯操作
path.pop_back();
}
}
int main()
{
vector<int> arr;
arr.push_back(2);
arr.push_back(3);
arr.push_back(5);
// arr.push_back(7);
int sum = 0;
backtracking(arr,8,sum,0);
for(int i=0;i<result.size();i++){
for(int j=0;j<result[i].size();j++){
cout<<result[i][j]<<" ";
}
cout<<endl;
}
system("pause");
return 0;
}
运行结果
40参考代码(测试用例2,5,2,1,2)
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int> candidates,int target,int sum,int index,vector<bool> used){
if(sum > target)
return;
if(sum == target){
result.push_back(path);
return;
}
for(int i=index;i<candidates.size();i++){
if(i>0 && candidates[i]==candidates[i-1] && used[i-1]==false)
continue;//重难点:去重操作
sum+=candidates[i];
path.push_back(candidates[i]);
used[i]=true;
backtracking(candidates,target,sum,i+1,used);
sum-=candidates[i];
path.pop_back();
used[i]=false;
}
}
int main()
{
vector<int> candidates;
candidates.push_back(2);
candidates.push_back(5);
candidates.push_back(2);
candidates.push_back(1);
candidates.push_back(2);
sort(candidates.begin(),candidates.end());
vector<bool> used(candidates.size(),false);
backtracking(candidates,5,0,0,used);
for(int i=0; i<result.size(); i++){
for(int j=0; j<result[i].size(); j++){
cout<<result[i][j]<<" ";
}
cout<<endl;
}
system("pause");
return 0;
}
重难点个人心得:
1.39题每个数可以被无限次使用,40,每个数只能使用一次;(index每次加1或不加1得以解决)
2.39题数组中无重复元素,而40题中允许有重复元素,这就会造成遍历时有重复的集合。
都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因。
那么问题来了,我们是要同一树层上使用过,还是同一树枝上使用过呢?
回看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。
所以我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。
为了理解去重我们来举一个例子,candidates = [1, 1, 2], target = 3,(方便起见candidates已经排序了)
强调一下,树层去重的话,需要对数组排序!不排序used数组没有意义了!!!
运行结果