输入:数组 N 正数和值 X , N 小于 X
输出:所有数字之和等于 Y>的子数组。 X ,这样就没有其他子数组的总数大于 X 但小于 Y 。
这个问题有多项式解决方案吗?如果是的话,你能介绍一下吗?
答案 0 :(得分:4)
正如其他答案所示,这是一个NP-Complete问题,称为“Knapsack Problem”。所以没有多项式解决方案。但它有一个伪多项式时间算法。 This explains what pseudo polynomial is
A visual explanation of the algorithm
如果这是与工作相关的(我已经多次遇到过这个问题,各种伪装)我建议引入额外的限制来简化它。如果这是一般性问题,您可能还想检查其他NP-Complete问题。 One such list.
修改1:
AliVar提出了一个很好的观点。给定的问题搜索Y> X和背包问题搜索Y< X.所以这个问题的答案需要更多的步骤。当我们试图找到Y>的最小和时。 X我们也在寻找S< (总计 - 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
代表a
在Sn
参与的所有总和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的值增加,这种情况不会以指数方式重复。因此,该过程以多项式时间终止。