这是一道枚举经典题。
本题有三种做法,各位需要根据每个题的数据范围来决定自己用哪种方法。
本题解中统一设最大和为Max。
方法一、 枚举子序列,从起点到终点求和。时间复杂度:O(n^3)
我们可以枚举它的子序列,也就是枚举它的长度、起点和终点。我们不妨设长度为i,起点为j,终点为k,当前子序列的和为s。于是我们就有了下列核心代码:
Max=a[1];//最大和初始化为第一个数 for(i=1;i<=n;i++)//枚举长度 for(j=i;j<=n;j++){//枚举起点 s=0;//当前序列和初始化为0 for(k=i;k<=j;k++)//枚举终点 s+=a[k];//计算和 if(s>Max)Max=s;//比较 } printf("%d",Max);//输出
这种方法在时限1s的题目中只能通过n<=450的范围。
但是本题的序列长度n<=200000。
所以采用该方法超时。
方法二、先求前缀和,再枚举。时间复杂度:O(n^2)
我们可以先求出从第一个数到当前数的和,然后枚举起点和终点,计算每一个子序列的和。于是我们就有了下列核心代码:
memset(s,0,sizeof(s));//初始化前缀和 for(i=1;i<=n;i++)s[i]=s[i-1]+a[i];//计算前缀和 Max=a[i];//初始化最大值为第一个数 for(i=1;i<=n;i++) for(j=i;j<=n;j++) Max=max(Max,s[j]-s[i-1]);//枚举起点和终点。 //注意是i-1!因为计算从第i个数开始的和需要减去前i-1个数的和 printf("%d",Max);
怎么有点像DP?
这种方法在时限1s的题目中只能勉强通过n<=10000的范围。
但是本题的序列长度n<=200000。
所以采用该方法也超时。
方法三、直接从头开始计算和,并每次记录最大和。时间复杂度:O(n)
不妨设s为以第i个数结尾的最大和,并每次计算和。
若当前和比最大值大,就更新最大值。
若当前和比0小,就把和清0。
核心代码:
scanf("%d",&n); s=0; Max=-2e9; for(i=1;i<=n;i++){ scanf("%d",&a); s+=a; if(s>Max)Max=s; if(s<0)s=0; } printf("%d",Max);
这种方法在时限1s的题目中能勉强通过n<=100000000的范围。
由于本题的序列长度n<=200000。
所以可以采用这种方法。
总结:一定要明白第三种方法的道理,并且能自己编写代码,这样水平才能有所提升。