echo

任生命穿梭 时间的角落

0%

组合总和

39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

1
2
3
4
5
6
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

示例 2:

1
2
3
4
5
6
7
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都是独一无二的。
  • 1 <= target <= 500

由于 candidates 是无序的,只能暴力搜索全部的解。考虑剪枝,先将 candidates 数组排序,在搜索的过程中如果得到的当前总和大于 target ,那么后面的元素总和都不用尝试(由于数组递增,它们的总和比大于当前总和)。

数字可以被重复选取代表下一次递归时,还可以取当前选取的数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
List<List<Integer>> ans = new ArrayList<List<Integer>>();

public List<List<Integer>> combinationSum(int[] candidates, int target) {
//排序是剪枝的基础
Arrays.sort(candidates);
dfs(0, target, candidates, new ArrayDeque<Integer>());
return ans;
}

private void dfs(int start, int target, int[] candidates, ArrayDeque<Integer> path){
int end = candidates.length - 1;
if(target == 0){
ans.add(new ArrayList(path));
return;
}

for(int i = 0; start + i <= end; i++){
int cur = candidates[start + i];
//剪枝
if(target - cur < 0){
break;
}else{
path.addLast(cur);
//只能使用当前元素及其右侧的值,避免重复解
dfs(start + i, target - cur, candidates, path);
path.removeLast();
}
}
}
}