最大子段和详解

 

问题的提出:

给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整均为负数时定义子段和为0,依此定义,所求的最优值为

Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n

 例如,当(a1,a2,a3,a4,a4,a6)=(-2,11,-4,13,-5,-2)时,最大子段和为20

方法一:

蛮力法

方法二:

分治法

算法描述如下:

 

针对最大子段和这个具体问题本身的结构,我们还可以从算法设计的策略上对上述O(n^2)计算时间算法进行更进一步的改进。从问题的解结构也可以看出,它适合于用分治法求解。

如果将所给的序列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)的最大子段和。据此可以设计出最大子段和问题的分治算法如下:

代码如下:

#include #define MAX 100 int maxsub(int left,int right); int a[MAX]; int main() { int i; int count; scanf("%d",&count); for(i=0;i=left;i--) { sum+=a[i]; if(sum>left_max) left_max=sum; } sum=0; right_max=0; for(i=center+1;i<=right;i++) { sum+=a[i]; if(sum>right_max) right_max=sum; } sum=right_max+left_max; if(sum

 

方法三:

动态规划法

算法描述如下:

 

在对于上述分治算法的分析中我们注意到,若记b[j]=max(a[i]+a[i+1]+..+a[j]),其中1<=i<=j,并且1<=j<=n。则所求的最大子段和为max b[j],1<=j<=n。

由b[j]的定义可易知,当b[j-1]>0时b[j]=b[j-1]+a[j],否则b[j]=a[j]。故b[j]的动态规划递归式为:

b[j]=max(b[j-1]+a[j],a[j]),1<=j<=n。

代码如下:

#include int main() { int count; int a[100]; int b[100]; int i; int max; scanf("%d",&count); for(i=0;i0) b[i]=b[i-1]+a[i]; else b[i]=a[i]; if(b[i]>max) max=b[i]; } printf("%d/n",max); return 0; } 

你可能感兴趣的:(算法,算法)