话说好不容易选上这门课了,却也已经错过第一次课了。作业也是妥妥的迟了,趁着中秋赶紧补上,望老师见谅。
参考书选择代码大全。
最大子数组和问题分析
一维情况
首先想到的应该是O(n^2)的算法(一般不会想到n^3方的算法吧。。略蛋疼)。用sum[i]表示数组中1到i的和,并令sum[0]=0。f[i]表示以i结尾的最大子数组和。那么有f[i]=Max{sum[i]-sum[j]|0<=j<=i-1}。最终结果是ans=Max{f[i]|1<=i<=n}。这个算法显然是O(n^2)的。
然后考虑对其进行优化。考虑到f[i]=Max{sum[i]-sum[j]|0<=j<=i-1}可以把只与i相关部分分离出来,变为f[i]=sum[i]-Min{sum[j]|0<=j<=i-1}。我们可以定义m[i]=Min{sum[j]|0<=j<=i},整个式子就变为f[i]=sum[i]-m[i-1]。只要sum和m都能很快求出,那么f也就能很快求出。显然sum可以O(n)的求出来,而m也可以在求sum的同时求出。所以sum和m的求解都是O(n)的。那么f的求解也就是O(n)的,在求f的同时更新最大值即可。
这个问题还有很多别的O(n)的做法,而且基本都比上面的做法更简洁,但上面这种方法应该是最容易想到的。在实现上其实不需要数组,全部使用临时变量即可,因为每读一个数据就可以求出所有需要的sum、m、f,具体见程序。
二维情况
见到高维的问题自然会想到将其转化为低维的问题。对于这个问题,只要先确定最大子块的上下界,就能将其转化为一维。上下界共有O(n^2)种可能,对每个上下界应用一维算法需要O(m)的时间,所以总共是O(n^2*m)的。实现时需要一个二维数组存储sum[i,j],表示(1,1)到(i,j)的和,其余都用临时变量即可。
代码
1 #include <stdio.h> 2 #define MAXN 100000000 3 FILE *fin,*fout; 4 int main(void) 5 { 6 fin=fopen("in.txt","r"); 7 fout=fopen("out.txt","w"); 8 int n,i,sum,m,ans,a,f; 9 10 fscanf(fin,"%d\n",&n); 11 12 sum=0; 13 m=0; 14 ans=-MAXN; 15 16 for(i=1;i<=n;i++) 17 { 18 fscanf(fin,",%d",&a); 19 sum+=a; 20 f=sum-m; 21 if(f>ans)ans=f; 22 if(sum<m)m=sum; 23 } 24 25 fprintf(fout,"%d\n",ans); 26 return 0; 27 }
1 #include <stdio.h> 2 #define MAXN 100000000 3 FILE *fin,*fout; 4 int sum[100][100]; 5 int main(void) 6 { 7 fin=fopen("in.txt","r"); 8 fout=fopen("out.txt","w"); 9 int n,m,i,j,k,a,ans,min,f; 10 11 fscanf(fin,"%d,%d,",&n,&m); 12 for(i=1;i<=n;i++) 13 for(j=1;j<=m;j++) 14 { 15 fscanf(fin,"%d,",&a); 16 sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a; 17 } 18 19 ans=-MAXN; 20 for(i=1;i<=n;i++) 21 for(j=i;j<=n;j++) 22 { 23 min=0; 24 for(k=1;k<=m;k++) 25 { 26 f=sum[j][k]-sum[i-1][k]-min; 27 if(f>ans) 28 ans=f; 29 if(sum[j][k]-sum[i-1][k]<min) 30 min=sum[j][k]-sum[i-1][k]; 31 } 32 } 33 34 fprintf(fout,"%d\n",ans); 35 return 0; 36 }
测试结果
由于时间比较紧,先借用一下forwil同学的数据。。
一维情况:
6, 5,6,-3,8,-9,2
输出结果 16
二维情况:
3, 6, 5,6,-3,8,-9,2 1,-12,20,0,-3,-5 -9,-7,-3,6,7,-1
输出结果 28