转载:最大子段和问题(Maximum Interval Sum)
一.问题描述
给定长度为n的整数序列,a[1...n], 求[1,n]某个子区间[i , j]使得a[i]+…+a[j]和最大.或者求出最大的这个和.
例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为[2,4].
如果该序列的所有元素都是负整数时定义其最大子段和为0。
二. 问题分析
1、最大子段和问题的简单算法:
2、最大子段和问题的分治法:
求子区间及最大和,从结构上是非常适合分治法的,因为所有子区间[start, end]只可能有以下三种可能性:
在[1, n/2]这个区域内
在[n/2+1, n]这个区域内
起点位于[1,n/2],终点位于[n/2+1,n]内
1 int DAC(int * array, int left, int right) 2 { 3 if (left == right) 4 return array[left]>0 ? array[left] : 0; 5 6 int center = ( left + right ) / 2; 7 int leftSum = DAC(array, left, center); 8 int rightSum = DAC(array, center+1, right); 9 10 int temp = 0; 11 int leftHalfMaxSum = 0; 12 for (int i=center;i>=left;--i) 13 { 14 temp += array[i]; 15 if (leftHalfMaxSum < temp) 16 leftHalfMaxSum = temp; 17 } 18 temp = 0; 19 int rightHalfMaxSum = 0; 20 for (int i=center+1;i<=right;++i) 21 { 22 temp += array[i]; 23 if (rightHalfMaxSum < temp) 24 rightHalfMaxSum = temp; 25 } 26 27 int max = leftSum > rightSum ? leftSum : rightSum; 28 return max > leftHalfMaxSum + rightHalfMaxSum ? max : leftHalfMaxSum + rightHalfMaxSum; 29 }
分治法的难点在于第三种情形的理解,这里应该抓住第三种情形的特点,也就是中间有两个定点,然后分别往两个方向扩张,以遍历所有属于第三种情形的子区间,求的最大的 一个,如果要求得具体的区间,稍微对上述代码做点修改即可. 分治法的计算时间复杂度为O(nlogn).
3、最大子段和问题的动态规划算法:
令b[j]表示以位置 j 为终点的所有子区间中和最大的一个
子问题:如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中
如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含
如果b[j-1]<=0,那么b[j] = a[j]。
对于这种子问题结构和最优化问题的证明,可以参考算法导论上的“剪切法”,即如果不包括子问题的最优解,把你假设的解粘帖上去,会得出子问题的最优化矛盾.证明如下:
令a[x,y]表示a[x]+…+a[y] , y>=x
假设以j为终点的最大子区间 [s, j] 包含了j-1这个位置,以j-1为终点的最大子区间[ r, j-1]并不包含其中
即假设[r,j-1]不是[s,j]的子区间
存在s使得a[s, j-1]+a[j]为以j为终点的最大子段和,这里的 r != s
由于[r, j -1]是最优解, 所以a[s,j-1]<a[r, j-1],所以a[s,j-1]+a[j]<a[r, j-1]+a[j]
与[s,j]为最优解矛盾.
1 int DP(int *a, int size) 2 { 3 int *b = new int[size]; 4 b[0] = a[0]; 5 int max = b[0]; 6 for (int i=1;i<size;++i) 7 { 8 if (b[i-1] > 0) 9 b[i] = b[i-1] + a[i]; 10 else 11 b[i] = a[i]; 12 13 if(b[i]>max) 14 max = b[i]; 15 } 16 return max; 17 }
测试代码:
1 #include "stdafx.h" 2 #include <stdlib.h> 3 #include "DivideAndConquer.h" 4 #include "DynamicProgramming.h" 5 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 int array[] = {-2, 11, -4, 13, -5, -2}; 10 //int result = DAC(array, 0, 5); 11 int result = DP(array, 6); 12 printf("%d", result); 13 system("pause"); 14 return 0; 15 }