Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
Note:
- All numbers (including target) will be positive integers.
- Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
- The solution set must not contain duplicate combinations.
For example, given candidate set 10,1,2,7,6,1,5
and target 8
,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]
思路和解法
整个解法分为iterative部分和recursive部分,两个部分加起来,构成完整的解法:
- 对当前节点所有可能进行遍历使用的是iterative方法。
- 对每一个当前节点的可能,去寻找之后节点的可能用recursive方法。
Combination Sum II 这一题,主体框架是上文说的iterative+recursive,但是由于对每个当前节点,可能的状态只有两种,即选择和不选择,因此代码中实际没有对节点状态用for循环,而是直接把两种状态写出来。
这一题除了主体部分,还要考虑的一个问题是避免重复。为了避免重复有两种方法:
方法1:
- 对当前节点,先选择自己,然后recurse到下一个节点
- 回来后,去除自己(即不选自己),然后去除接下来和自己重复的节点,到第一个不重复的节点开始做recursion
核心是:如果不选某一个节点,则不选接下来的和该节点相同的节点
code如下:
https://leetcode.com/discuss/11278/my-solution-without-using-set
void comb(vector<int>& num, int st, int target, vector<int>& psol, vector<vector<int> >& result){
//processing num[st...] , psol: partial solution
if(target==0){
result.push_back(psol);
}else{
if( st==num.size() ) return; //cannot make it return directly
int newtarget = target - num[st];
if(newtarget>=0){
psol.push_back(num[st]);
comb(num, st+1, newtarget, psol, result);
psol.pop_back();
int i=st+1;
while( i<num.size() && num[i]==num[i-1] ) { i++; } //skip dups
comb(num, i, target, psol, result); //skip all values == num[st]
}//else cannot make it stop immediately
}
}
vector<vector<int> > combinationSum2(vector<int> &num, int target) {//O(2^n) time + O(n) space
vector<vector<int> > result;
vector<int> psol;
std::sort(num.begin(), num.end());
comb(num, 0, target, psol, result);
return result;
}
方法2:
- 对当前节点,先不选自己,然后recurse到下一个节点。
- 回来后,选自己,并选择接下来和自己一样的节点,然后在和自己不一样的节点处,做recursion。
核心:如果选择某一个节点,则一定选择接下来跟它相同的所有节点。
code如下(我的解法):
class Solution {
public:
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
vector<int> pre;
vector<vector<int>> res;
sort(num.begin(), num.end());
trySum(num, pre, target, 0, res);
return res;
}
void trySum(vector<int> &num, vector<int> &pre, int target, int index, vector<vector<int>> &res) {
int len = num.size();
if(target==0) {
res.push_back(pre);
return;
} else if(index>=len || target<num[index]) { // 这一句是一个重要的剪枝,如果当前节点比target都大了,由于已经排序了,那么后面的节点也一定比target大,那么也没有必要继续往后找了
return;
}
if(index>=len) return;
trySum(num, pre, target, index+1, res);
int new_index = 0;
for(new_index=index; new_index<len; new_index++) { // if we choose A, we must also choose the A's following it. This is designed to avoid duplicate combinations
if(num[new_index] == num[index]) {
target -= num[index];
} else {
break;
}
}
int ori_size = pre.size();
pre.insert(pre.end(), new_index-index, num[index]);
trySum(num, pre, target, new_index,res);
pre.erase(pre.begin()+ori_size, pre.end());
return;
}
};