描述
给定一个无重复元素的数组 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]
]
分析
这是一道回溯题,一开始想成完全背包了orz也不知道是怎么回事
很多看似复杂的题都可以用回溯法来解决,而回溯法是有解题思路的
首先明确解空间是什么
在这道题里就是从给定的数据里选任意个数组成的排列
回溯的终点即判断标准:和==target
可以的选项:给定的数据
为了不出现重复的排列,对原有数组排序,每次保证新添加的数不超过当前数的最后一个
或者每次遍历从当前数开始,前面的数不再遍历
代码
class Solution {
public:
vector<vector<int> >res;
void backtrack(vector<int>cur, int target, int sum, vector<int>candidates)
{
if (sum == target) { //和达到要求
res.push_back(cur);
return;
}
//不可能的解,直接跳过
if(target < candidates[0])return;
//可选的数据
for (int i = 0; i < candidates.size() && candidates[i] <= target; i++)
{
int num = candidates[i];
//防止出现 2 3 3 | 3 2 2这种情况,保证由小到大
if (cur.size() >= 1)
{
int top = cur[cur.size() - 1];
if (num < top)
continue;
}
//判断条件
if (sum + num <= target )
{
//更新状态
sum += num;
cur.push_back(num);
backtrack(cur, target, sum, candidates);
//回溯
cur.pop_back();
sum -= num;
}
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
vector<int>cur;
backtrack(cur, target, 0, candidates);
vector<vector<int> > ans;
ans = res;
return ans;
}
};
优化
注意在target直接小于排序数组的最小数时可以直接跳过,加这一条都可以使时间迅速减少
Python的更简洁版本
class Solution:
def backtrack(self,ans, cur, candidates, target,l):
if target == 0:
ans.append(list(cur))
return
if target < candidates[0]:
return
r = len(candidates)
for i in range(l, r):
num = candidates[i]
target -= num
cur.append(num)
self.backtrack(ans, cur, candidates, target, i)
cur.remove(num)
target += num
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
ans = []
cur = []
candidates = sorted(candidates)
self.backtrack(ans, cur, candidates, target, 0)
return ans