求一个长度为n的数组A {A0, A1...An-1}的最大子段和。
为了避免没有意义的容错,我们认为n > 0 且子段长度 > 0。
任意0≤ i ≤ j≤n的下角标组合就可以确定一个子段,也就确定了一个和,一共有1+2+...+n = (n+1)*n/2个和。
设一个子段(闭区间)的累加和为sum(i,j)
sum(i, j)=Ai+...+ Aj
我们按照区间结尾分组,第 i 组有 i+1 行
0 | ... | k | k+1 | ... |
sum(0,0) | ... | sum(0,k) | sum(0,k+1)=sum(0,k)+Ak+1 | ... |
... | sum(1,k) | sum(1,k+1)=sum(1,k)+Ak+1 | ... | |
... | ... | ... | ... | |
sum(k,k) | sum(k,k+1)=sum(k,k)+Ak+1 | ... | ||
sum(k+1,k+1)=Ak+1 | ... |
上表可以表示所有的sum,我们求最大的。
假设我们已知第k 组(以k 结尾的区间)sum的最大值,设为result(k),不需要知道具体是哪一个区间累加出来的。
第k+1组黄色部分,前面每一个值都是第k组对应值加上Ak+1。一个数组,每个数都加上同一个数,最大的那个还是最大的那个.
第k组的最大值是result(k),那第k+1组黄色部分的最大值就是result(k)+Ak+1。常数时间O(1)就能得出。
最后一行也是常数时间得出,所以整组的最大值就是max{ result(k)+Ak+1 , Ak+1 }
Ak+1 要不要加上result(k)就看正负就行了,所以就是result(k+1) = result(k) < 0 ? Ak+1 : result(k) + Ak+1
result(0)=sum(0,0)=A0 也是常数时间得出。由result(0),常数时间可以得出result(1)=result(0) < 0 ? A1 : result(0) + A1,以此类推。
O(n)的时间就能得出所有组的最大值,再从这些最大值里取最大的那个就可以了。
下面附上C语言代码。
1 int max_sum(int[] a, int n) { 2 int result=a[0], group_result=a[0]; 3 for(int i=1; i<n; i++) { 4 group_result = group_result < 0 ? a[i] : group_result + a[i]; 5 if(group_result > result) 6 result = group_result ; 7 } 8 return result; 9 }