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]
]
题解:
与 数组总和 的区别就是:有重复数字,每个数字只能用一次。
延续 数组总和 的思路,上一题中因为元素可以重复使用,所以我们在递归时,没有改变下标,即:
dfs( candidates, i, now + candidates[i], target );
而这题每个元素只能使用一次,所以递归时需要修改下标,即:
dfs( candidates, i + 1, now + candidates[i], target );
还有一个最重要的问题:因为有重复元素,如果我们还是按照 数组总和 的代码来,肯定有重复的答案。
举个例子:candidates=[1, 1, 2, 5, 6, 7, 10], target = 8
在从 pos = 0
开始搜索时,找到一个答案 [1, 2, 5]
。如果接着从 pos = 1
开始搜索,又会找到 [1, 2, 5]
,出现重复答案。
解决办法很简单,只需要在循环中加一个判断:
if ( i > p && candidates[i] == candidates[i - 1] ) continue;
这样会不会漏掉什么情况呢?答案是不会,具体在纸上画画就知道了。
代码:
class Solution {
public:
vector<vector<int>> ret;
vector<int> ans;
void dfs( vector<int>& candidates, int p, int now, int target ) {
if ( now == target ) {
ret.push_back( ans );
return;
}
if ( p >= candidates.size() ) return;
for ( int i = p; i < candidates.size(); ++i ) {
if ( i > p && candidates[i] == candidates[i - 1] ) continue;
if ( now + candidates[i] <= target ) {
ans.push_back( candidates[i] );
dfs( candidates, i + 1, now + candidates[i], target );
ans.pop_back();
} else return;
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort( candidates.begin(), candidates.end() );
if ( !candidates.size() || candidates[0] > target ) return {};
dfs( candidates, 0, 0, target );
return ret;
}
};
/*
时间:0ms,击败:100.00%
内存:10.4MB,击败:96.99%
*/