广义目标和问题

时间:2011-08-02 17:53:58

标签: algorithm data-structures

给定一个整数x和一个未排序的整数数组,描述一个算法 确定两个数字是否加起来为x。 (在这种情况下,不允许使用哈希表)。 解决方案是:

对数组进行排序。然后,跟踪数组中的两个指针,一个在 一开始,一开始。每当前两个整数的总和是 小于x,向前移动第一个指针,只要总和大于x, 向后移动第二个指针。如果找不到两个添加到x的数字 在其中一个指针遇到之前,则没有一对与x相加的整数。这个 解决方案需要O(n log n)时间,因为我们对数字进行排序。

我们可以为k整数提供通用解决方案吗?上面说的问题适用于k=2。我现在想要找3整数target sum,依此类推。

3 个答案:

答案 0 :(得分:2)

只是为此设置一个上限...对于固定的k,问题总是在P(数字列表的大小的多项式,无论如何),并且可以通过简单的O求解( n ^ k)算法:生成k集(它们有C(n,k))并检查它们。对于k = 2的情况,这对应于生成所有n(n + 1)/ 2个两组并检查它们。

如果我们改为k <= n,则此问题等同于子集和问题,因此NP完全。

请注意,这些表示复杂性的严格上限...对于k = 2,您已经找到了O(n log n)算法,并且该方法可以推广到更高的k。

Edit2:删除了一些更严格的界限,因为看起来我的构造错了。抱歉搞砸了。支持spinning_plate的道具让我保持诚实。

答案 1 :(得分:0)

如果您可以使用任何数据结构,则只需将其视为knapsack problem,并跟踪除了总和之外您使用的数字。

numbers = [xxx]
buckets = [[0,0] for x in range(MAX_SUM)]
buckets[0][0] = 1;
for number in numbers:
    for bucketi in range(MAX_SUM):
        if buckets[bucketi][0] == 1 and buckets[bucketi][1] < k: 
           buckets[bucketi+number][0] = 1;
           buckets[bucketi+number][1] = buckets[bucketi][1] + 1;

这也是对@Patrick所得到的尝试,我不确定这一点,但这是一个有趣的想法。

def go_(numbers, range_bottom,range_top,sum_target,k,min_v,max_v,nums):
    min_v_,max_v_,res_ = go(numbers, range_bottom,range_top,sum_target-sum(nums),k)
    min_v = min(min_v,min_v_)
    max_v = max(max_v,max_v_)
    if len(res_) > 0:
        newres = [x for x in res_]
        newres = newres+nums
        return [0,0,newres]
    return [min_v,max_v,res_]

def go(numbers, range_bottom,range_top,sum_target,k):

    if sum_target==0 and k == 0:
        return [0,0,['-']];
    elif sum_target<0 or k==0 or range_bottom == range_top:
        return [sum_target,sum_target,[]]


    min_v = 666; max_v = -666;

    if range_top-range_bottom>1:
        min_v ,max_v, res_ = go_(numbers, range_bottom+1, range_top-1, sum_target,k-2,min_v,max_v,[numbers[range_bottom],numbers[range_top-1\
]])
        if len(res_):

            return [0,0,res_];

    if not ( min_v<0 and max_v<0 ):
        min_v ,max_v, res_ = go_(numbers, range_bottom+1, range_top, sum_target ,k,min_v,max_v ,[])
        if len(res_):


            return [0,0,res_];


    if not ( min_v>0 and max_v>0 ):
        min_v ,max_v, res_ = go_(numbers, range_bottom, range_top-1, sum_target,k,min_v,max_v,[])
        if len(res_):

            return [0,0,res_];

    return [min_v,max_v,res_]

答案 2 :(得分:0)

我感觉这会有大量的挫折,因为我不会说数学(或CS语言),但我只是想出如何扩展你的方法
我假设

  1. 需要不同的整数集(无重复)
  2. 每当sum = x,我存储满足sum = x的整数并继续前进,所以     我得到满足sum = x
  3. 的所有整数元组

    第A节

    k = 3且x =总和

    按升序对数组进行排序 将first_pointer放在第一个整数上,将second_pointer放在第二个整数上(即第一个指针旁边) 将third_pointer放在最后一个整数上。

    1. 计算sum = first_pointer + second_pointer + third_pointer。
    2. 如果总和&lt; x然后second_pointer ++重复步骤1.
    3. 如果总和&gt; x然后third_pointer--重复步骤1.
    4. 重复步骤1到3,直到second_pointer&gt; = third_pointer,即指针相遇。
    5. first_pointer ++; second_pointer = first_pointer + 1; third_pointer =最后一个整数,即将first_pointer移位一步(在右侧)并将second_pointer放在第一个指针旁边,将第三个指针返回到最后一个整数。
    6. 重复步骤1到5,直到第5步second_pointer = third_pointer。
    7. 如果你到达这里,没有找到任何sum = x那么就没有解决方案(我可能在这里错了,但我没有看到任何其他可能性。)
    8. <小时/>

      B节

      表示k = 4且x =总和

      按升序对数组进行排序 将前两个指针放在 A部分中 将第三个和第四个放在最后两个整数上,即第三个指针= second_last_integer和fourth_pointer = last_integer。

      1. 类似于 A部分第1步 sum =指针(第1 +第2 +第3 +第4)
      2. 类似于第A部分第2步
      3. 如果总和&gt;
      4. 这是不同的) x然后是fourth_pointer--
      5. 类似于第A节第4步
      6. 现在完全不同)为了清楚起见,我将其分为5.a和5.b 5.a这部分类似于 A部分,因为您将移动第一个和第二个指针使它们彼此相邻并重复 B部分的步骤1到4
        5.b如果你没有找到总和yet_pointer--; third_pointer = fourth_pointer-1并重复步骤1到4 和5.a
      7. 重复5.b直到second_pointer = third_pointer。
      8. 如果你到达这里没有任何sum = x,那么就没有解决方案//标准免责声明

        <小时/>

        C节

        (我的屏幕空间很小,因为我看不到我正在写的内容的预览。所以我会保持简短,让它保持你的想象力)
        表示任何k和x =总和

        按升序对数组进行排序 在开头放置第一个int(k / 2)指针 将其余的k-int(k / 2)指针放在末尾。

        1. 计算sum = sigma(k)。
        2. 如果总和&lt; x然后指针(int(k / 2))++。从步骤1开始重复。
        3. 如果总和&gt; x然后指针(k - int(k / 2)) - 。从步骤1开始重复。
        4. 从步骤1重复到指针(int(k / 2))&gt; =指针(k-int(k / 2))。
        5. 现在取[int(k / 2) - 1,int(k / 2),(k - int(k / 2)),(k - int(k / 2)+ 1)]指针并继续类似于 B部分
        6. 通过将int(k / 2) - 2向右移动,然后向左移动(k - int(k / 2)+ 2)来重复上述步骤。
        7. 延长步骤6直到你移动所有指针&lt; int(k / 2),以便它们重叠
        8. 结束

          // p !!写了这么多!!