我们都听说过解决最大子序列和的宾利美丽的编程珍珠问题:
maxsofar = 0;
maxcur = 0;
for (i = 0; i < n; i++) {
maxcur = max(A[i] + maxcur, 0);
maxsofar = max(maxsofar, maxcur);
}
如果我们添加一个小于 M 的附加条件最大子序列怎么办?
我们都听说过解决最大子序列和的宾利美丽的编程珍珠问题:
maxsofar = 0;
maxcur = 0;
for (i = 0; i < n; i++) {
maxcur = max(A[i] + maxcur, 0);
maxsofar = max(maxsofar, maxcur);
}
如果我们添加一个小于 M 的附加条件最大子序列怎么办?
这可以使用动态规划来解决,尽管只能在伪多项式时间内解决。
定义
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]))
这个解法类似于背包问题的解法。
这应该这样做。我是赖特吗?
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
存在。
如果全部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 每次迭代并从那里前进。
可在 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