给定n个整数(可能为负整数)a1,a2,a3……an.求形如 ai,a(i+1),……,aj i,j=1,……n,i<=j
的子段和的最大值。当所有的整数均为负整数的时候定义其最大子段和为0,例如:当(a1,a2,a3,a4,a5,a6)=(-2,11,-4,13,-5,-2)时,最大子段和为 i=2,j=4(下标从1开始)。
枚举所有可能的起始下标(i=1,2,3,……,n)和终止下标(j=i,i+1……,j),累加i到j所有元素的和,并从中选取最大的子段和。
partsum()
{
int i,j,n,t,a[100];
print("input number of data(<99):");
input(n);
print("input",n,"data:");
for(i=1;i<=n;i++)
{
input(a[i]);
}
i=j=1;
t=max_sum(a,n,&i,&j);
print("The most sum of section is",t);
print("starting point is",i);
print("end point is",j);
}
max_sum(int a[],int n,int *best_i,int *best_j)
{
int i,j,k,this_sum,sum;
sum=0;
*best_i=0;
*best_j=0;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
this_sum=0;
for(k=i;k<=j;k++)
{
this_sum = this_sum+a[k];
}
if(this_sum>sum)
{
sum=this_sum;
*best_i=i;
*best_j=j;
}
}
}
return sum;
}
如果将所给的序列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:j],且1<=i<=(n/2),(n/2)+1<=j<=n
(1)和(2)可递归求得,对于(3)而言,序列中的元素a[(n/2)]与a[(n/2)+1]一定在最大字段中。因此,可以计算出a[i:(n/2)]的最大值s1;并计算出a[(n/2)+1:j]中的最大值s2.则s1+s2即为(3)的最优值。
partsum()
{
int i,j,n,t,a[100];
print("input number of data(<99):");
input(n);
print("input",n,"data:");
for(i=1;i<=n;i++)
{
input(a[i]);
}
i=j=1;
t=max_sum(a,n,&i,&j);
print("The most sum of section is",t);
print("starting point is",i);
print("end point is",j);
}
int max_sum(int a[],int n)
{
return max_sub_sum(a,1,n);
}
max_sub_sum(int a[],int left,int right)
{
int center,i,j,left_sum,right_sum,sum,s1,s2,lefts,rights;
if(left=right)
{
if(a[left]>0)
{
return a[left];
}
else
{
return 0;
}
}
else
{
center=(left+right)/2;
left_sum=max_sub_sum(a,left,center);
right_sum=max_sub_sum(a,center+1,right);
s1=0;//情形(3)的处理
lefts=0;
for(i=center;i>=left;i--)
{
lefts=lefts+a[i];
if(lefts>s1)
{
s1=lefts;
}
}
s2=0;
rights=0;
for(i=center+1;i<=right;i++)
{
rights=rights+a[i];
if(rights>s2)
{
s2=rights;
}
}
if(s1+s2<left_sum and right_sum<left_sum)
{
return left_sum;
}
if(s1+s2<right_sum)
{
return right_sum;
}
return s1+s2;
}
}
用动态规划法解决问题的思路很简单,就是通过开辟存储空间,存储各个子问题的计算结果,从而避免重复的计算。其实就是用空间效率去换取时间效率。
记sum[i]为a[1]~a[i]的最大字段和,记this_sum[i]为当前子段和。
this_sum[i]从i=1开始计算,当this_sum[i-1]>=0时,前面字段的和对总和有贡献,所以要累加当前元素的值;当this_sum[i-1]<0时,前面字段的和对总和没有贡献,要重新开始累加,以后的子段和从i开始。sum[i]在记录a[1]~a[i]的最大字段和,不断存储新得到的较大的this_sum[i]。
初值:this_sum[0]=0; i=1,2……n时
this_sum[i]=this_sum[i-1]+a[i] ——> 当this_sum[i-1]>=0
this_sum[i]=a[i] ——> 当this_sum[i-1]<0
相应地sum[i]的递推式如下:a[0]=0,i=1,2……n时,
sum[i]=sum[i-1] ——> 当this_sum[i]<=sum[i-1]
sum[i]=this_sum[i] ——> 当this_sum[i]>sum[i-1]
partsum()
{
int i,j,n,t,a[100];
print("input number of data(<99):");
input(n);
print("input",n,"data:");
for(i=1;i<=n;i++)
{
input(a[i]);
}
i=j=1;
t=max_sum(a,n,&i,&j);
print("The most sum of section is",t);
print("starting point is",i);
print("end point is",j);
}
max_sub_sum(int a[],int n,int *best_i,int *best_j)
{
int i,j,this_sum[n+1],sum[n+1];
this_sum[0]=0;
*best_i=0;
*best_j=0;
i=1;
for(j=1;j<=n;j++)
{
this_sum[j] = this_sum[j-1]+a[j];
if(this_sum[j]>sum[j])
{
sum[j]=this_sum[j];
*best_i=i;
*best_j=j;
}
else if(this_sum[j]<0)
{
i=j+1;
this_sum[j]=0;
}
}
return sum;
}
在上述算法的空间上进行优化,存储a[1]~a[j]的当前子段和的this_sum及当前最大子段和的sum都不必设置为n个元素的数组,用普通变量就可以实现了。因为在递推的过程中,只需保存一个值就足够了。
partsum()
{
int i,j,n,t,a[100];
print("input number of data(<99):");
input(n);
print("input",n,"data:");
for(i=1;i<=n;i++)
{
input(a[i]);
}
i=j=1;
t=max_sum(a,n,&i,&j);
print("The most sum of section is",t);
print("starting point is",i);
print("end point is",j);
}
max_sub_sum(int a[],int n,int *best_i,int *best_j)
{
int i,j,this_sum,sum;
this_sum=0;
*best_i=0;
*best_j=0;
i=1;
for(j=1;j<=n;j++)
{
this_sum = this_sum+a[j];
if(this_sum>sum)
{
sum=this_sum;
*best_i=i;
*best_j=j;
}
else if(this_sum<0)
{
i=j+1;
this_sum=0;
}
}
return sum;
}