给定一个无重复元素的数组 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]
]
这道题目是求集合,并不是求极值,因此动态规划不是特别切合。这种题目其实有一个通用的解法,就是回溯法,回溯法其实来源于深度优先搜索dfs,模板:
1, 以当前位置为源流往下摸排所有可以跳到的位置
2, 最终递归返回源流位置
3, 然后再以下面一个位置作为源流位置,重复上述操作
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
# 先排序,便于后面剪枝
candidates.sort()
# 最终结果集合
res = []
# temp用来记录路径
temp = []
self.dfs(res,candidates, temp, target, 0)
return res
# 记住回溯法的模板
def dfs(self, res, candidates, temp, target, index):
# target为剩余和,从candidates的index开始往下摸排,temp为路径记录
# 递归边界
# 当满足节点和等于target的时候(剩余和=0),
# 此时路径中的所有节点可以算是一条符合要求的路径,可加入最终结果集合,
# 然后return
if target == 0:
res.append(temp.copy())
return
# 剪枝
# 因为事先已经排序过了,所以当剩余和为负数的时候(路径中最后一个节点,
# 也就是最大的那个,如果都比剩余和大的话,剩余要摸排的也就是index及以后的节点,
# 会更大,导致剩余和为负),
# 再往下摸排只会让剩余和越来越小,此时不需要再往下查找了,return
elif (temp and target < temp[-1]):
return
for i in range(index, len(candidates), 1):
# 把当前节点加入路径
temp.append(candidates[i])
# 以这个节点为源往下摸排,看看再加入哪些节点能满足节点和等于target
self.dfs(res, candidates, temp, target-candidates[i], i)
# 这条源流摸排结束,将这个节点弹出,再加入下一个节点并以此为源重新往下摸排
temp.pop()