题目
给定一个数组 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] ]
思路
直观的思路仍是直接使用笔记4中的枚举法,但简单的思考就得知时间复杂度为O(2^n*n),实际上是非常差的,尤其对本题,可以使用剪枝。
例如 candidates = [10,1,2,7,6,1,5], target = 8, 很直观的就看出,凡是带有元素10的子集,都不能满足相加为8的结果,因此这部分需要剪枝。如果使用笔记4的方法,对于大样本一定会超时。
故,本题使用回溯法更佳。
在搜索回溯过程中进行剪枝操作:递归调用时,计算已选择元素的和sum,若sum>target,不再进行更深的搜索,直接返回。
答案
class Solution {
public:
vector<vector<int>> result;
vector<int> item; //独立子集的记录
set<vector<int>> res_set; //用于剔除重复结果
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
findResult(0, candidates,0,target);
//将结果导入vector中
for (set<vector<int>>::iterator it = res_set.begin(); it != res_set.end(); it++)
result.push_back(*it);
return result;
}
void findResult(int i, vector<int>& nums,int sum,int target) {
//当元素已选完 或item中的元素和sum超过target 结束
//大于target不再进行为剪枝
if (i>=nums.size()||sum>target)
return;
sum = sum + nums[i];
item.push_back(nums[i]);
//当item中的元素和为target且该结果未添加时
//set自动剔除重复元素
if (target == sum )
res_set.insert(item);
findResult(i+1, nums, sum, target);
sum = sum - nums[i]; //回溯后 sum将nums[i]减去并从item中删除
item.pop_back();
findResult(i + 1, nums, sum, target);
}
};
#include <iostream>
#include <set>
#include <vector>
#include <algorithm> //标准算法的头文件
using namespace std;
class Solution {
public:
vector<vector<int>> result;
vector<int> item; //独立子集的记录
set<vector<int>> res_set; //用于剔除重复结果
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
findResult(0, candidates,0,target);
//将结果导入vector中
for (set<vector<int>>::iterator it = res_set.begin(); it != res_set.end(); it++)
result.push_back(*it);
return result;
}
void findResult(int i, vector<int>& nums,int sum,int target) {
//当元素已选完 或item中的元素和sum超过target 结束
//大于target不再进行为剪枝
if (i>=nums.size()||sum>target)
return;
sum = sum + nums[i];
item.push_back(nums[i]);
//当item中的元素和为target且该结果未添加时
//set自动剔除重复元素
if (target == sum )
res_set.insert(item);
findResult(i+1, nums, sum, target);
sum = sum - nums[i]; //回溯后 sum将nums[i]减去并从item中删除
item.pop_back();
findResult(i + 1, nums, sum, target);
}
};
void test01()
{
Solution solution;
vector<int>v0 = { 10,1,2,7,6,1,5 };
vector<vector<int>> ret = solution.combinationSum2(v0,8);
cout << ret.size() << endl;
//迭代器遍历
for (vector<vector<int>>::iterator it = ret.begin(); it != ret.end(); it++) {
for (vector<int>::iterator subit = (*it).begin(); subit != (*it).end(); subit++)
cout << (*subit) << "\t";
cout << endl;
}
}
int main()
{
test01();
system("pause");
return 0;
}