【问题标题】:find elements summing to s in an array在数组中查找总和为 s 的元素
【发布时间】:2011-04-19 00:29:22
【问题描述】:

给定一个元素数组(所有元素都是唯一的),给定一个总和 s 找到所有总和为 s 的子集。 对于前数组{5,9,1,3,4,2,6,7,11,10} 总和是 10 可能的子集是{10}, {6,4}, {7,3}, {5,3,2}, {6,3,1} 等。 可能还有更多。 还可以找到这些子集的总数。 请帮我解决这个问题..

【问题讨论】:

  • 作为这个似乎的作业,我建议发布你迄今为止尝试过的内容,以便我们帮助你完成这项工作
  • 这是 NP 完整的。然后进行详尽的搜索。
  • @missingno:搜索不一定是详尽的:您不需要使用输入数组中值大于目标总和的元素
  • @MarcoS:当然你可以在很多情况下修剪搜索树,或者尝试在这里和那里利用一些 simetry(以提高实践中的运行时间),但没有这样的策略会使渐近行为比在这种情况下进行详尽的搜索。

标签: algorithm


【解决方案1】:

这是一个著名的回溯问题,可以通过递归来解决。基本上它是一种蛮力方法,其中尝试了每种可能的组合,但至少给出了 3 个边界条件来修剪搜索。
这是算法:
s 变量,表示到目前为止选择的元素的总和。
r 变量,用于剩余数组的总和。
M 是所需的总和。
k 是从 0 开始的索引
w 是给定整数的数组

Sum(k,s,r)
{
    x[k]:=1;  //select the current element
    if(s<=M & r>=M-s & w[k]<=M-s)
    then
    {
        if(s+w[k]==M)
        then output all i [1..k] that x[i]=1
        else
        sum(k+1,s+w[k],r-w[k])
    }
    x[k]:=0  //don't select the current element
    if(s<=M) & (r>=M-s) & (w[k]<=M-s)
    then
    {
        if (M==s)
        then output all i [1..k] that x[i]=1
        else
        sum(k+1,s,r-w[k])
    }
}

我正在使用数组“x”来标记为解决方案选择的候选编号。在每一步检查 3 个边界条件:

1. Sum of selected elements in "x" from "w" shouldn't exceed M. s<M.    
2. Remaining numbers in array should be able to complete M. r>=M-s.
3. Single remaining value in w shouldn't overflow M. w[k]<=M-s.  

如果任一条件失败,则终止该分支。

【讨论】:

    【解决方案2】:

    这里有一些 python 代码可以做你想做的事。它广泛使用了 itertools,所以要了解它,您可能需要查看 itertools docs

    >>> import itertools
    >>> vals = (5,9,1,3,4,2,6,7,11,10)
    >>> combos = itertools.chain(*((x for x in itertools.combinations(vals, i) if sum(x) == 10) for i in xrange(len(vals)+1)))
    >>> for c in combos: print c
    ...
    (10,)
    (9, 1)
    (3, 7)
    (4, 6)
    (5, 1, 4)
    (5, 3, 2)
    (1, 3, 6)
    (1, 2, 7)
    (1, 3, 4, 2)
    

    它的作用基本上是这样的:

    • 对于所有可能的子集大小 - for i in xrange(len(vals)+1),请执行以下操作:
    • 迭代所有具有此大小的子集 - for x in itertools.combinations(vals, i)
    • 测试子集值的总和是否为 10 - if sum(x) == 10
    • 在这种情况下产生子集

    对于每个子集大小,都会产生另一个生成器,因此我使用 itertools.chain 将它们链接在一起,这样就有一个生成器产生所有解决方案。

    由于您只有一个生成器而不是列表,因此您需要在对其进行迭代时对元素进行计数 - 或者您可以使用 list(combos) 将生成器中的所有值放入一个列表中(这会消耗生成器,所以不要'不要尝试在此之前/之后对其进行迭代)。

    【讨论】:

    • 如何在生成器生成的连击中找到 no.of 元素? @ThiefMaster
    【解决方案3】:

    由于你没有说是不是作业,我只给出一些提示:

    • nums 成为您可以使用的数字数组(在您的示例中为nums = {5,9,1,3,4,2,6,7,11,10}

    • targetSum 成为给定的总和值(在您的示例中为targetSum = 10

    • sort nums:您不想使用比targetSum 更大的nums 元素搜索解决方案

    • S_s 是一组取自nums 的整数,其和等于s

    • R_s成为所有S_s的集合

    • 你想找到R_s(在你的例子中是R_10

    • 现在,假设您有一个函数 find(i, s),它使用从位置 i 开始的 nums 的子数组返回 R_s

      • 如果nums[i] &gt; s你可以停止(记住你之前已经排序nums

      • 如果nums[i] == s你找到了R_s = { { nums[i] } },那么返回它

      • 对于您要计算的每个j in [1 .. nums.length - 1] R_s' = find(i + j, targetSum - nums[i]),然后将nums[i] 添加到R_s' 中的每个集合,并将它们添加到您的结果R_s

    • 通过实现find 并调用find(0, 10) 来解决您的问题

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-07
      • 1970-01-01
      • 2021-10-14
      • 1970-01-01
      • 2022-01-09
      • 1970-01-01
      • 2020-02-22
      相关资源
      最近更新 更多