最大连续和
给出一个长度为n的序列A1,A2,A3,...An,求最大连续和。换句话说,要求找到1=<i=<j=<n,使得Ai+...+Aj尽量大。
题目似乎很简单,但是在n非常大的时候(如n>10000)时,采用最习惯使用的暴力过的话似乎就有一些难度,
下面逐步给出了几种方法,解决问题在可以解出来时,应该找求最优解
方法一:枚举
直接在i到j之间一一枚举。复杂度O(n^3)
1 #include<stdio.h> 2 int main() 3 { 4 int a[1000],n; 5 while(scanf("%d",&n)!=EOF) 6 { 7 int i; 8 for(i=0;i<n;i++) 9 scanf("%d",&a[i]); 10 int j,k,best=a[0];//初始化最大值 11 for(i=0;i<n-1;i++) 12 for(j=i;j<n;j++) 13 { 14 int s=0; 15 for(k=i;k<=j;k++)s+=a[k]; 16 if(best<s)best=s; 17 } 18 printf("%d\n",best); 19 } 20 return 0; 21 }
方法二:处理后枚举
连续子序列的和等于两个前缀和之差。复杂度O(n^2)
1 #include<stdio.h> 2 int main() 3 { 4 int n,a[1000],s[1000]; 5 while(~scanf("%d",&n)) 6 { 7 int i,j; 8 for(i=0;i<n;i++) 9 scanf("%d",&a[i]); 10 int best=a[0]; 11 s[0]=0; 12 for(i=0;i<n;i++)s[i+1]=s[i]+a[i];//递归求前缀和S 13 for(i=0;i<n;i++) 14 for(j=i;j<n;j++) 15 best=best>(s[j+1]-s[i])?best:(s[j+1]-s[i]); 16 printf("%d\n",best); 17 } 18 return 0; 19 }
方法三:分治法
划分问题:把问题实例划分为子问题
递归求解:递归解决子问题
合并问题:合并子问题的解得到原问题的解
复杂度O(nlogn)
1 #include<stdio.h> 2 int maxsum(int *a,int x,int y)//返回数组左闭右开区间[x,y)中最大连续和 3 { 4 int i,m,v,L,R,max; 5 if(y-x==1)return a[x];//只有一个元素,直接返回 6 m=x+(y-x)/2;//分治第一步,划分为[x,m)与[m,y)两部分 7 int l=maxsum(a,x,m);//分治第二步,递归求解 8 int r=maxsum(a,m,y); 9 max=l>r?l:r; 10 v=0;L=a[m-1];//第三步,合并子问题 11 for(i=m-1;i>=x;i--){v+=a[i];L=L>v?L:v;} 12 v=0;R=a[m]; 13 for(i=m;i<y;i++){v+=a[i];R=R>v?R:v;} 14 return max>(L+R)?max:(L+R);//把子问题与L和R比较 15 } 16 int main() 17 { 18 int a[1000],n; 19 while(~scanf("%d",&n)) 20 { 21 int i; 22 for(i=0;i<n;i++) 23 scanf("%d",&a[i]); 24 int best=maxsum(a,0,n); 25 printf("%d\n",best); 26 } 27 return 0; 28 }
方法四:稍作处理
把O(n^2)的代码稍作处理,得到复杂度O(n)的算法
1 #include<stdio.h> 2 int main() 3 { 4 int n,a[1000],s[1000]; 5 while(~scanf("%d",&n)) 6 { 7 int i; 8 for(i=0;i<n;i++) 9 scanf("%d",&a[i]); 10 s[0]=a[0]; 11 int maxs=s[0],mins=s[0]; 12 for(i=1;i<n;i++) 13 { 14 s[i]=s[i-1]+a[i]; 15 maxs=maxs>s[i]?maxs:s[i]; 16 mins=mins<s[i]?mins:s[i]; 17 } 18 printf("%d\n",maxs-mins); 19 } 20 return 0; 21 }