算法描述如下
如果将所给的序列a[1:n]分为长度相等的两段a[1:n/2]和a[n/2+1:n],分别求出这两段的最大子段和,则a[1:n]的最大子段和有三种情况:
(1) a[1:n]的最大子段和与a[1:n/2]的最大子段和相同
(2) a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同
(3) a[1:n]的最大子段和为a[i]+…+a[j],并且1<=i<=n/2,n/2+1<=j<=n。
对于(1)和(2)两种情况可递归求得,但是对于情况(3),容易看出a[n/2],a[n/2+1]在最大子段中。因此,我们可以在a[1:n/2]中计算出s1=max(a[n/2]+a[n/2-1]+…+a[i]),0<=i<=n/2,并在a[n/2+1:n]中计算出s2= max(a[n/2+1]+a[n/2+2]+…+a[i]),n/2+1<=i<=n。则s1+s2为出现情况(3)的最大子段和。据此可以设计出最大子段和问题的分治算法如下:
时间复杂度:O(NlogN)
#include<stdio.h>
int maxsum(int a[], int left, int right)
{
if (left == right) return a[left];
else
{
int mid = (right + left) / 2;
int leftsum = maxsum(a, left, mid);
int rightsum = maxsum(a, mid + 1, right);
int rights = 0, s1 = 0, lefts = 0, s2 = 0;
for (int i = mid + 1; i <= right; i++)//右边
{
rights += a[i];
if (rights > s1) s1 = rights;
}
for (int i = mid; i >= left; i--)//左边
{
lefts += a[i];
if (lefts > s2) s2 = lefts;
}
if ((s1 + s2) < leftsum) return leftsum;
if ((s1 + s2) < rightsum) return rightsum;
}
}
int main()
{
int a[] = { -5,11,-4,13,-4,-2 };
printf("%d\n", maxsum(a, 0, 5));
return 0;
}
输出:
20