给定一个整数数组,你怎么能找到两个指数i和j,这样在索引开始和结束时子元素的总和最大化,在线性时间?
答案 0 :(得分:11)
简单。假设您获得了数组a
。首先,计算数组s
,其中s[i] = a[0]+a[1]+...+a[i]
。你可以在线性时间内完成:
s[0]=a[0];
for (i=1;i<N;i++) s[i]=s[i-1]+a[i];
现在,总和a[i]+a[i+1]+..+a[j]
等于s[j]-s[i-1]
。对于固定的j
,要最大化此差异的值,您应该在s[i-1]
的范围内找到最小0..(j-1)
。
想象一下通常的算法来找到数组中的最小值。
min = x[0];
for (j=1; j<N; j++)
if (x[j] < min)
min = x[j];
迭代并将每个数组元素与min
进行比较......但在每次迭代时,此min
是数组中的最小值,其中索引范围为0..j
!这就是我们正在寻找的东西!
global_max = a[0];
max_i = max_j = 0;
local_min_index = 0;
for (j=1; j<N; j++){
// here local_min is the lowest value of s[i], where 0<=i<j
if (s[j] - s[local_min_index] > global_max) {
global_max = s[j] - s[local_min_index]
//update indices
max_i = local_min_index + 1;
max_j = j;
}
//update local_min_index for next iteration
if (s[j]<local_min){
local_min = s[j];
// update indices
local_min_index = j;
}
}
答案 1 :(得分:8)
来自我programming pearls的副本:
maxsofar = 0
maxendinghere = 0
for i = [0, n)
/* invariant: maxendinghere and maxsofar are accurate
are accurate for x[0..i-1] */
maxendinghere = max(maxendinghere + x[i], 0)
maxsofar = max(maxsofar, maxendinghere)
答案 2 :(得分:1)
这个python代码返回序列的边界。就原始问题而言,i=bestlo
,j=besthi-1
。
#
# given a sequence X of signed integers,
# find a contiguous subsequence that has maximal sum.
# return the lo and hi indices that bound the subsequence.
# the subsequence is X[lo:hi] (exclusive of hi).
#
def max_subseq(X):
#
# initialize vars to establish invariants.
# 1: best subseq so far is [bestlo..besthi), and bestsum is its sum
# 2: cur subseq is [curlo..curhi), and cursum is its sum
#
bestlo,besthi,bestsum = 0,0,0
curlo,curhi,cursum = 0,0,0
for i in xrange(len(X)):
# extend current subseq and update vars
curhi = i+1
cursum += X[i]
if cursum <= 0:
#
# the current subseq went under water,
# so it can't be usefully extended.
# start fresh at next index.
#
curlo = curhi
cursum = 0
elif cursum > bestsum:
# adopt current subseq as the new best
bestlo,besthi,bestsum = curlo,curhi,cursum
return (bestlo,besthi)
以下是此代码通过的一些doctest示例。
r'''
doctest examples:
>>> print max_subseq([])
(0, 0)
>>> print max_subseq([10])
(0, 1)
>>> print max_subseq([-1])
(0, 0)
>>> print max_subseq(xrange(5))
(1, 5)
>>> print max_subseq([-1, 1, -1])
(1, 2)
>>> print max_subseq([-1, -1, 1, 1, -1, -1, 1, 2, -1])
(6, 8)
>>> print max_subseq([-2, 11, -4, 13, -5, -2])
(1, 4)
>>> print max_subseq([4, -3, 5, -2, -1, 2, 6,-4])
(0, 7)
'''
答案 3 :(得分:1)
你实际上需要Kadane的算法修改来记住子数组的下限和上限,这里是C ++ 11代码:
#include <iostream>
#include <vector>
typedef std::pair<std::vector<int>::iterator, std::vector<int>::iterator> SubSeq;
SubSeq getMaxSubSeq(std::vector<int> &arr) {
SubSeq maxSequence{arr.begin(), arr.begin()};
auto tmpBegin = arr.begin();
int maxEndingHere = 0;
int maxSoFar = 0;
for(auto it = arr.begin(); it < arr.end(); ++it) {
int currentSum = maxEndingHere + *it;
if(currentSum > 0) {
if(maxEndingHere == 0) {
tmpBegin = it;
}
maxEndingHere = currentSum;
} else {
maxEndingHere = 0;
}
if(maxEndingHere > maxSoFar) {
maxSoFar = maxEndingHere;
maxSequence.first = tmpBegin;
maxSequence.second = it + 1;
}
}
return maxSequence;
}
int main()
{
std::vector<int> arr{-1, 2, 90, -50, 150, -300, 56, 12};
auto seq = getMaxSubSeq(arr);
while(seq.first != seq.second) {
std::cout << *(seq.first) << " ";
++(seq.first);
}
return 0;
}