最大连续负和或最小正子序列和问题

时间:2011-07-12 15:54:26

标签: algorithm max programming-pearls

我们都听说过宾利美丽的编程珍珠问题 它解决了最大子序列总和:

maxsofar = 0;
maxcur = 0;
for (i = 0; i < n; i++) {
  maxcur = max(A[i] + maxcur, 0);
  maxsofar = max(maxsofar, maxcur);
}

如果我们添加一个较小M的附加条件最大子序列怎么办?

4 个答案:

答案 0 :(得分:1)

这应该这样做。我怀疑吗?

int maxsofar = 0;
for (int i = 0; i < n - 1; i++) {
   int maxcur = 0;
   for (int j = i; j < n; j++) {
      maxcur = max(A[j] + maxcur, 0);
      maxsofar = maxcur < M ? max(maxsofar, maxcur) : maxsofar;
   }
}

不幸的是,这是O(n^2)。你可以通过在maxcur >=M时打破内循环来加快速度,但仍然n^2仍然存在。

答案 1 :(得分:1)

如果全部A[i] > 0,您可以在O(n lg n)中执行此操作:预计算部分和S[i],然后对S进行二元搜索S[i] + M。例如:

def binary_search(L, x):
  def _binary_search(lo, hi):
    if lo >= hi: return lo
    mid = lo + (hi-lo)/2
    if x < L[mid]:
      return _binary_search(lo, mid)
    return _binary_search(mid+1, hi)
  return _binary_search(0, len(L))

A = [1, 2, 3, 2, 1]
M = 4
S = [A[0]]
for a in A[1:]:
  S.append(S[-1] + a)
maxsum = 0
for i, s in enumerate(S):
  j = binary_search(S, s + M)
  if j == len(S):
    break
  sum = S[j-1] - S[i]
  maxsum = max(sum, maxsum)
print maxsum

编辑:正如atuls正确指出的那样,二元搜索是过度的;因为S在增加,我们可以跟踪每次迭代的j并从那里前进。

答案 2 :(得分:1)

这可以使用动态编程来解决,尽管只是在伪多项式时间内。

定义

m(i,s) := maximum sum less than s obtainable using only the first i elements

然后,您可以使用以下递归关系计算max(n,M)

m(i,s) = max(m(i-1,s), m(i-1,s-A[i]]+A[i]))

此解决方案类似于背包问题的解决方案。

答案 3 :(得分:0)

可在O(n log(n))中求解。使用二叉搜索树(平衡)搜索大于sum-M的最小值,然后通过从左到右更新min,并插入sum。其中sum是到目前为止的部分金额。

  best = -infinity;
  sum = 0;
  tree.insert(0);
  for(i = 0; i < n; i++) {
     sum = sum + A[i];
     int diff = sum - tree.find_smallest_value_larger_than(sum - M);
     if (diff > best) {
       best = diff;
     }
     tree.insert(sum);
   }

   print best