最大部分列和を解くベントレーの美しいプロラミング パール問題については、誰もが聞いたことがあるでしょう。
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]))
この解決策は、ナップザック問題の解決策に似ています。
すべて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 を追跡し、そこから先に進むことができます。
これはこれを行う必要があります。私はライトですか?
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
残ります。
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