参考书选择
选了电子版---[代码大全2英文版(完整清晰版)].chm
穷逼没钱买书好烦躁T.T
问题分析
一维的情况
最朴素最暴力的方法是枚举所有的sum[i,j],算出其值然后取最大。
时间复杂度为O(N^3),注意到sum[i,j]=sum[i,j-1]+a[i,j],所以不必每个sum[i,j]都重新算一遍,只需要通过前面的值来递推即可。时间复杂度降为O(N^2)
我们对所有的sum[i,j]来分类,用tail[j]表示所有的sum[i,j]的最大值,即tail[j]=max{sum[i,j]|1<=i<=j},表示所有由a[j]结尾的子串中的最大的和。
那么我们的目标转化为求最大的tail,不过这样分析起来似乎没有任何的优化。
不过我们发现,假设tail[j-1]已经求出了,那么tail[j]只有取两种情况:
- 以tail[j-1]和a[j]组成新的子串
- 放弃tail[j-1],重新由a[j]为一单独的新串。
因此我们得到如下状态转移方程:
tail[j]=max{tail[j-1]+a[j],a[j]}
求解问题只需从小到大遍历一遍O(N)的状态空间,每次转移需要O(1)时间复杂度,所以总共只有O(N)时间复杂度。
二维的情况
在有了一维度的解答之后,我们很容易想到通过枚举上下界来把二维的问题转化为一维,需要枚举C(N,2)+N种上下界,每一种需要O(M)时间复杂度来计算,因此总共需要O(N^2*M)的时间复杂度
编程实现
一维的情况
1 #include<stdio.h> 2 #define MAXN 100000 3 #define MINV (-(1<< 31 -1)) 4 int maxsum(int p[],int size) 5 { 6 int i,sum,ans; 7 ans=MINV; 8 sum=0; 9 for (i=0;i<size;i++) 10 { 11 if (sum<0) 12 sum=0; 13 sum+=p[i]; 14 if (sum>ans) 15 ans=sum ; 16 } 17 return ans; 18 } 19 int main() 20 { 21 int i,n,p[MAXN]; 22 freopen("input1.txt","r",stdin); 23 freopen("output1.txt","w",stdout); 24 scanf("%d,",&n); 25 for(i=0;i<n;i++) 26 scanf("%d,",&p[i]); 27 printf("%d\n",maxsum(p,n)); 28 return 0; 29 }
二维的情况
1 #include<stdio.h> 2 #define MAXN 1000 3 #define MAXM 1000 4 #define MINV (-(1<< 31 -1)) 5 int n,m,a[MAXN][MAXM]; 6 int maxsum(int N,int M) 7 { 8 int i,j,k,sum,ans; 9 int p[M]; 10 for (i=0;i<M;i++) p[i]=0; 11 ans=MINV; 12 sum=0; 13 for (i=0;i<N;i++) 14 { 15 for(j=i;j<N;j++) 16 { 17 sum=0; 18 for (k=0;k<M;k++) 19 { 20 p[k]+=a[j][k]; 21 if (sum<0) 22 sum=0; 23 sum+=p[k]; 24 if (sum>ans) 25 ans=sum; 26 } 27 } 28 for (k=0;k<M;k++)p[k]=0; 29 } 30 return ans; 31 } 32 int main() 33 { 34 int i,j; 35 freopen("input2.txt","r",stdin); 36 freopen("output2.txt","w",stdout); 37 scanf("%d,",&n); 38 scanf("%d,",&m); 39 for(i=0;i<n;i++) 40 for(j=0;j<m;j++) 41 { 42 scanf("%d,",&a[i][j]); 43 //printf("%d ",a[i][j]); 44 } 45 printf("%d\n",maxsum(n,m)); 46 return 0; 47 }
测试结果
input1.txt
6, 5,6,-3,8,-9,2
output1.txt
16
input2.txt
3, 6, 5,6,-3,8,-9,2 1,-12,20,0,-3,-5 -9,-7,-3,6,7,-1
output2.txt
28