题目名称:最大子段和
题目描述:给出一段序列,选出其中连续且非空的一段使得这段和最大。
输入格式:
第一行是一个正整数N,表示了序列的长度。
第2行包含N个绝对值不大于10000的整数A[i],描述了这段序列。
输出格式:
仅包括1个整数,为最大的子段和是多少。子段的最小长度为1。
枚举
最蠢的办法,枚举左端点和右端点,再求和,用一个max储存历史的最大值,就可以了。时间复杂度O(n3)。

1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int _Max(int x,int y){return x>y?x:y;} 5 int num[10001],sum=0; 6 int max=0; 7 int main(){ 8 int n; 9 scanf("%d",&n); 10 int i,j; 11 for(i=1;i<=n;i++){ 12 scanf("%d",&num[i]); 13 } 14 for(i=1;i<n;i++){ 15 for(j=i+1;j<=n;j++){ 16 sum=0; 17 for(int k=i;k<=j;k++) 18 sum+=num[k]; 19 max=_Max(sum,max); 20 } 21 } 22 printf("%d\n",max); 23 return 0; 24 }
在枚举的基础上,我们可以发现:求一个和为最大的子序列,那么作为任意一个子序列,若它的左端点是第i个元素,右端点是第j个元素,则它其中的元素之和可以表示为第1~j个元素和第1~i个元素之差。
这就是一个预处理。可以在枚举的基础上减少一些时间,但并不是最优,也是O(n2)。
分治
用分治求最大子段和应首先将这整个问题划分成规模更小的子问题,可以取中点,将这一个序列划分成长度相等的两个序列。对于此时的最大子段和,会出现3种情况,即:
1.最大子段和在第一个序列中,即在我们所取的中点左边。
2.最大子段和在第二个序列中,即在我们所取的中点右边;
3.最大子段和在第一个序列与第二个序列之间,即我们取的这个点被包含在这个子段中。这时,我们就应该继续以这个子段为基础,继续划分,知道不能划分为止。
(其实上面两种情况也是要划分的)(滑稽)
将3种情况的最大子段和合并,取三者之中的最大值就为问题的解。时间复杂度为O(nlogn)。

1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int n,a[20001]; 5 int Sum(int left, int right){ 6 int sum=0; 7 if(left==right){ 8 if(a[left]>0) 9 sum=a[left]; 10 else sum=0; 11 } 12 else{ 13 int mid=(left+right)/2; 14 int l_sum=Sum(left,mid); 15 int r_sum=Sum(mid+1,right); 16 int s1=0,s2=0; 17 int lefts=0,rights=0; 18 for(int i=mid;i>=left;i--){ 19 lefts+=a[i]; 20 if(lefts>s1)s1=lefts; 21 } 22 for(int j=mid+1;j<=right;j++){ 23 rights+=a[j]; 24 if(rights>s2)s2=rights; 25 } 26 sum=s1+s2; 27 if(sum<l_sum)sum=l_sum; 28 if(sum<r_sum)sum=r_sum; 29 } 30 return sum; 31 } 32 int main(){ 33 scanf("%d",&n); 34 int i,j; 35 for(i=1;i<=n;i++) 36 scanf("%d",&a[i]); 37 printf("%d",Sum(1,n)); 38 return 0; 39 }
贪心
和动态规划(乖乖的先看下面吧)的思路类似,博主看了很久,觉得好像没有区别,所以就不讲思路了。自己看代码思考吧。

1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int main(){ 5 int n,max=-2147483645,num,sum=0; 6 scanf("%d",&n); 7 for(int i=1;i<=n;i++){ 8 scanf("%d",&num); 9 sum+=num; 10 if(sum>max)max=sum; 11 if(sum<0)sum=0; 12 } 13 printf("%d",max); 14 return 0; 15 }
动态规划
枚举以从1到i为右端点的子段,用数组number[i]来保存第i个元素的值,sum[i]来保存以第i个元素结尾的最大子段和。显然,我们要求的就是sum整个数组中最大的一个数了。递归的边界条件:若i=1,则sum[i]=1。因为只有它一个元素。若i>=1,最优的整个子段只有这个元素本身。因为sum[i]是用来保存以第i个元素为右端点的最大子段和,所以这时的最大子段和就是number[i]和number[i]+sum[i-1]的比较,比较取最大即可。为什么要加上sum[i-1]呢?因为相比于以第i-2个元素结尾的子段来说,以i-1个元素结尾的子段显然要更长,结果更大的可能性也更大。状态转移方程:sum[i]=max(sum[i-1]+number[i],numberp[i]); (i>1)

1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int _Max(int x,int y){return x>y?x:y;} 5 int number[200001],sum[200001]; 6 int main(){ 7 int i,j; 8 int n,maxx=0; 9 scanf("%d",&n); 10 for(i=1;i<=n;i++) 11 scanf("%d",&number[i]); 12 sum[1]=number[1]; 13 maxx=sum[1]; 14 for(i=2;i<=n;i++){ 15 sum[i]=_Max(sum[i-1]+number[i],number[i]); 16 maxx=_Max(sum[i],maxx); 17 } 18 printf("%d",maxx); 19 }
其实这个题目还有很多方法,这里就不介绍其他的了。