跳转至

40. 组合总和 II

难度:中等

题目

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次

注意: 解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,

输出:

[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,

输出:

[
[1,2,2],
[5]
]

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

Reference

题解

由于每个数字只能使用一次,并且不能返回重复的组合,因此,可以统计每个数字的个数,逐个数字进行深度优先的搜索。搜索的终止条件为:

  1. 当前的和等于目标值,此时相当于我们找到了一个有效的解,将其记录下来。
  2. 当前的和大于目标值,此时说明当前的组合不可能是有效的解,直接返回。
  3. 当前的数字已经超过了目标值,直接返回。

在剩余情况下,我们需要考虑当前数字的个数\(n\),逐个判断使用当前数字\(0\)次到\(n\)次的情况。如果使用某一次后找到了有效解,就不再需要继续搜索其他次数。一种思路是从1逐个地考虑到\(n\)

class Solution:
    def dfs(
        self, k: int, end: int, counter: Dict[int, int], target: int,
        current_seq: List[int], results: List[List[int]]
    ):
        if target == 0:
            results.append(current_seq)
            return True
        if k > end or target < 0:
            return False
        v = counter.get(k, 0)
        for number in range(min(v, target // k) + 1):
            if self.dfs(
                k + 1, end, counter, target - k * number,
                current_seq + [k] * number, results
            ):
                break
        return False

    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        counter = Counter([_ for _ in candidates if _ <= target])
        result = []
        self.dfs(1, target, counter, target, [], result)
        return result

如上方法会导致大量无效迭代,可以通过只在出现的数字中进行寻找,减少无效的搜索。

class Solution:
    def dfs(
        self, k_idx: int, k_list: List[int], counter: Dict[int, int], target: int,
        current_seq: List[int], results: List[List[int]]
    ):
        if target == 0:
            results.append(current_seq)
            return True
        if k_idx >= len(k_list) or target < 0:
            return False
        k = k_list[k_idx]
        v = counter.get(k, 0)
        for number in range(min(v, target // k) + 1):
            if self.dfs(
                k_idx + 1, k_list, counter, target - k * number,
                current_seq + [k] * number, results
            ):
                break
        return False

    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        counter = Counter([_ for _ in candidates if _ <= target])
        result = []
        k_list = [*counter]
        self.dfs(0, k_list, counter, target, [], result)
        return result

评论