组合总和深度优先搜索解决方案

时间:2018-03-13 05:07:24

标签: algorithm recursion depth-first-search

给定正整数和目标值的列表,生成解集。例如,如果列表为[10, 1, 2, 7, 6, 1, 5]且目标为8,则解决方案集为...

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

我知道有多种解决方案,例如dp,但我试图让我的dfs解决方案工作,我相信我非常接近,但我无法得到正确的结果。如果可能的话,如果你没有太多改变我的初步答案,我希望如果不可能,那么任何解决方案都可以做到。

def combinationSum(self, candidates, target):
    candidates.sort()
    total = []
    self.helper(candidates, 0, target, [], total)

def helper(self, candidates, curr, target, temp, total):
    if target == 0:
        total.append(temp)
        return

    if target < 0:
        return

    for i in range(curr, len(candidates)):
        # avoid duplicates
        if i > curr and candidates[i] == candidates[i-1]:
            continue
        temp.append(candidates[i])
        self.helper(candidates, i+1, target-candidates[i], temp, total)
        # I'm not sure what to do here

这显然不能给我正确的结果,但我确实认为我正朝着生成解决方案集的方向前进。在递归调用删除不必要的元素之后,我根本不明白我需要做什么。

2 个答案:

答案 0 :(得分:2)

我认为这与你要做的事情一致:

def solve(target, sum, candidates, answer):
    if sum == target:
        print answer
        return

    if len(candidates) == 0 or sum > target:
        return

    first = candidates[0]
    count = candidates.count(first);

    answer.append(first)
    solve(target, sum+first, candidates[1:], answer)  #using the current number
    answer.pop()
    solve(target, sum, candidates[count:], answer)    #skipping the current number and any duplicates

if __name__ == "__main__":
    candidates = [10, 1, 2, 7, 6, 1, 5]
    candidates.sort();
    solve(8, 0, candidates, [])

关键是solve有两个递归调用。

第一个递归调用使用candidates列表中的第一个数字。所以它

  1. 将第一个数字附加到答案
  2. 将第一个数字添加到sum
  3. 仅从候选列表中删除第一个数字 传递到下一个级别
  4. 第二次递归调用不使用candidates列表中的第一个数字。由于它不使用第一个数字,因此它也不会使用第一个数字的任何副本。这就是count变量的原因。 candidates.count(first)是列表中等于first的条目数。因此,在递归调用candidates[count:]中删除first元素和任何重复项。 (这假定列表已排序,应在调用solve之前完成一次)。

答案 1 :(得分:1)

这里有一个使用递归的可能解决方案 - 我选择了一个元组来表示组合,但你也可以使用列表

def combinationSum (l, target, sum = 0, comb = ()):

  # base case: empty input [l]
  if not l:
    return []

  # inductive case: [l] has at least one element
  else:

    # [x] is the first sub-problem
    # [xs] is the rest of the sub-problems
    x, *xs = l

    # [x] plus [sum] is bigger than [target]
    if x + sum > target:
      return \
        combinationSum (xs, target, sum, comb)

    # [x] plus [sum] is smaller than [target]
    elif x + sum < target:
      return \
        combinationSum (xs, target, sum + x, (x, *comb)) + \
        combinationSum (xs, target, sum, comb)

    # [x] plus [sum] is equal to [target]
    else:
      return \
        [ (x, *comb)  ] + \
        combinationSum (xs, target, sum + x, (x, *comb)) + \
        combinationSum (xs, target, sum, comb)

data = [10, 1, 2, 7, 6, 1, 5]

print (combinationSum (data, 8))
# [(5, 2, 1), (7, 1), (1, 6, 1), (6, 2), (5, 1, 2), (1, 7)]

如果您希望combinationSum允许重复值,则只需更改一个部分。注意,程序考虑例如(5, 1, 1, 1)解决方案3次,因为1出现在3个唯一位置。如果您只想让(5, 1, 1, 1)出现一次,那么您必须考虑采用不同的方法。

...
    elif x + sum < target:
      return \
        combinationSum (xs, target, sum + x, (x, *comb)) + \
        combinationSum (l , target, sum + x, (x, *comb)) + \
        combinationSum (xs, target, sum, comb)
...
print (combinationSum (data, 8))
# [ (1, 1, 1, 1, 1, 1, 1, 1)
# , (1, 1, 1, 1, 1, 1, 1, 1)
# , (2, 1, 1, 1, 1, 1, 1)
# , (1, 1, 1, 1, 1, 1, 1, 1)
# , (1, 2, 1, 1, 1, 1, 1)
# , (1, 1, 1, 1, 1, 1, 1, 1)
# , (2, 2, 1, 1, 1, 1)
# , (1, 1, 2, 1, 1, 1, 1)
# , (1, 1, 1, 1, 1, 1, 1, 1)
# , (1, 2, 2, 1, 1, 1)
# , (1, 1, 1, 2, 1, 1, 1)
# , (1, 1, 1, 1, 1, 1, 1, 1)
# , (5, 1, 1, 1)
# , (2, 2, 2, 1, 1)
# , (1, 1, 2, 2, 1, 1)
# , (1, 1, 1, 1, 2, 1, 1)
# , (6, 1, 1)
# , (1, 1, 1, 1, 1, 1, 1, 1)
# , (5, 1, 1, 1)
# , (1, 2, 2, 2, 1)
# , (1, 1, 1, 2, 2, 1)
# , (1, 1, 1, 1, 1, 2, 1)
# , (5, 2, 1)
# , (7, 1)
# , (1, 6, 1)
# , (1, 1, 1, 1, 1, 1, 1, 1)
# , (5, 1, 1, 1)
# , (2, 2, 2, 2)
# , (1, 1, 2, 2, 2)
# , (1, 1, 1, 1, 2, 2)
# , (6, 2)
# , (1, 1, 1, 1, 1, 1, 2)
# , (5, 1, 2)
# , (1, 7)
# , (1, 1, 6)
# , (1, 1, 1, 1, 1, 1, 1, 1)
# , (5, 1, 1, 1)]
# ]