总和大于给定值的子阵列的最小总和

时间:2016-02-16 15:13:20

标签: arrays algorithm performance numbers

输入:数组 N 正数和值 X N 小于 X
输出:所有数字之和等于 Y>的子数组。 X ,这样就没有其他子数组的总数大于 X 但小于 Y

这个问题有多项式解决方案吗?如果是的话,你能介绍一下吗?

3 个答案:

答案 0 :(得分:4)

正如其他答案所示,这是一个NP-Complete问题,称为“Knapsack Problem”。所以没有多项式解决方案。但它有一个伪多项式时间算法。 This explains what pseudo polynomial is

A visual explanation of the algorithm

And some code

如果这是与工作相关的(我已经多次遇到过这个问题,各种伪装)我建议引入额外的限制来简化它。如果这是一般性问题,您可能还想检查其他NP-Complete问题。 One such list.

修改1:

AliVar提出了一个很好的观点。给定的问题搜索Y> X和背包问题搜索Y< X.所以这个问题的答案需要更多的步骤。当我们试图找到Y>的最小和时。 X我们也在寻找S< (总计 - X)。第二部分是原始的背包问题。所以;

  1. 查找总数
  2. 解决S<背包的问题。 (总计 - X)
  3. 从原始输入中减去背包解决方案中的项目列表。
  4. 这应该给你最小的Y> X

答案 1 :(得分:2)

A成为我们的数组。这是一个O(X*N)算法:

initialize set S = {0}
initialize map<int, int> parent
best_sum = inf
best_parent = -1
for a in A
     Sn = {}
     for s in S
         t = s + a
         if t > X and t < best_sum
             best_sum = t
             best_parent = s
         end if
         if t <= X
             Sn.add(t)
             parent[t] = s
         end if
     end for
     S = S unite with Sn
end for

要以最佳总和打印元素,请打印数字:

Subarray = {best_sum - best_parent}
t = best_parent
while t in parent.keys()
    Subarray.add(t-parent[t])
    t = parent[t]
end while
print Subarray

这个想法类似于动态编程的想法。我们只计算所有可达的(可以作为子阵列和获得的那些)总和小于X。对于数组a中的每个元素A,您可以选择参与总和。在更新步骤S = S unite with Sn S代表aSn参与的所有总和a期间未参与的所有金额。

如果此项目在集合中,您可以将S表示为布尔数组,将项目设置为true。请注意,此布尔数组的长度最多为X

总体而言,该算法为O(X*N),内存使用率为O(X)

答案 2 :(得分:1)

我认为这个问题是NP难的,子集和可以减少到它。这是我的减少: 对于具有集合S = {x1,...,xn}的子集和的实例,期望找到具有和t的子集。假设d是两个不相等的xi和xj之间的最小距离。构建S'= {x1 + d / n,...,xn + d / n}并将其提供给您的问题。假设您的问题找到答案;即S'的子集D',其中和Y> t是具有该属性的最小和。将D'的原始成员集命名为D.可能发生三种情况: 1)Y = t + | D | * d / n,这意味着D是原始子集和问题的解。 2)Y> t + | D | * d / n表示原始问题无法找到答案集。 3)Y < t + | D | * d / n。在这种情况下,指定t = Y并重复该问题。由于新t的值增加,这种情况不会以指数方式重复。因此,该过程以多项式时间终止。