Description
Given a sequencea[1],a[2],a[3]......a[n], your job is to calculate the max sum of asub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is6 + (-1) + 5 + 4 = 14.
Input
The first lineof the input contains an integer T(1<=T<=20) which means the number oftest cases. Then T lines follow, each line starts with a numberN(1<=N<=100000), then N integers followed(all the integers are between-1000 and 1000).
Output
For each testcase, you should output two lines. The first line is "Case #:", #means the number of the test case. The second line contains three integers, theMax Sum in the sequence, the start position of the sub-sequence, the endposition of the sub-sequence. If there are more than one result, output thefirst one. Output a blank line between two cases.
Sample Input
2
5 6 -1 5 4 -7
7 0 6 -1 1 -6 7-5
Sample Output
Case 1:
14 1 4
Case 2:
7 1 6
最大字段和一直都有很多做法:
1. 枚举算法
简单的枚举算法当然可以解决这个问题,枚举所有可能的起始下标(i=1,2,3……,n)和终止下标(j=1,2,3……,n),累加各种情况的元素和,并从中选出最大的字段和。
2. 二分策略算法
如果将所给的序列a[1:n]分为长度相等的两段a[1: (n/2)]]和b[(n/2)+1:n],分别求出这两段的最大字段和,则a[1:n]的最大字段和有3种情形。
(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)和(2)可递归求得。
对于情形(3),序列中的元素a[(n/2)]与a[(n/2)+1]一定在最大子段中。因此可以算出a[i: (n/2)]的最大值S1;并计算出a[(n/2)+1:j]中的最大值S2。则S1+S2即为出现情况(3)时的最优值。
据此可设计最大子段和的分治算法求出最大子段。由于子问题不独立,不同于一般的二分治算法,这里算法的实质是“三”分治。
intmax_sum3(int a[],int n)
{
return max_sub_sum(a,1,n);
}
intmax_sub_sum(int a[],int left,int right)
{
intcenter,i,j,sum,left_sum,right_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&&right_sum<left_sum)//情形1
return left_sum;
if(s1+s2<right_sum)//情形2
return right_sum;
return s1+s2;//情形3
}
}
3. 动态规划算法
方法2中由于分解后子问题不独立,情形3中重复计算较多,没有充分利用前期的计算结果。而动态规划的特长就是解决分解的子问题不独立的情况。用动态规划就是通过开辟存储空间,存储各个子问题的计算结果,从而避免重复计算。并且动态规划有很强的阶段递推思想,用前一阶段存储的计算结果递推后一阶段的结果。
记sum[i]为a[1]~a[i]的最大子段和,记this_sum[i]为当前子段和。
this_sum[i]从i=1开始计算,当this_sum[i-1]>=0时,前面子段的和对总和有作用,所以要累加当前元素的值;当this_sum[i-1]<=0时,则开始重新累加。
初值: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]
sum[i]在记录a[1]~a[i]的最大子段和。
由此思想得出的完全代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int a[100001];
int maxSub(int *a,int n) //求最大子段和函数
{
int i=0, max=0,max_pos=0;
int si_1=0,si=0;
int *p = (int *)malloc(n*sizeof(int));//通过p来记录this_sum[i-1]的状态
if (p==NULL)
return -1;
max =si_1 = a[0]; //初始化max,sum[i-1]
p[0] = 0; //p[i]=0表示this_sum[i-1]<0
for (i=1; i<n; i++)
{
if (si_1<0){ //sum[i-1]<0的情况
p[i] = 0;
si = a[i]; //si即sum[i]
} else{
p[i] = 1;
si = si_1+a[i]; //累加
}
si_1 = si;
if (si>max){ //记录最大子段和
max = si;
max_pos = i; //记录最大子段终点
}
}
for (i=max_pos; i>=0; i--)
if (p[i]==0) //寻找最大字段起点
break;
printf("%d %d %d\n", max,i+1,max_pos+1);
free(p);
p= NULL;
return max;
}
int main()
{
int n,i,m,j;
scanf("%d",&m);
getchar();
for(i=0;i<m;i++)
{
scanf("%d",&n);
for(j=0;j<n;j++)
scanf("%d",&a[j]);//输入数列
printf("Case%d:\n",i+1);
maxSub(a, n);
if((i!=m-1))
printf("\n");
memset(a,0,sizeof(a));
}
return 0;
}