http://acm.hdu.edu.cn/showproblem.php?pid=1024(到这里提交)
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4977 Accepted Submission(s): 1627
1 3 1 2 32 6 -1 4 -2 3 -2 3
68HintHuge input, scanf and dynamic programming is recommended.
最终程序代码:
1 #include < stdio.h >
2 int m,n,a[ 1000001 ],ans;
3 int f[ 1000001 ],sum[ 1000001 ],b[ 2 ][ 1000001 ];
4 int main(){
5 int i,j,k,i1,i2;
6 while (scanf( " %d%d " , & m, & n) != EOF)
7 {
8 sum[ 0 ] = 0 ;
9 for (i = 1 ;i <= n;i ++ )
10 {
11 scanf( " %d " , & a[i]);
12 sum[i] = sum[i - 1 ] + a[i];
13 }
14
15 for (j = 0 ;j <= n;j ++ )b[0][j]=0;————————前0个数分成j段的最大和为0
16 for ( j = 0; j <=n; j++) b[1][j]= -2000000000; //已改动
17 i1 = 1 ;ans =- 2000000000 ;
18
19 for (i = 1 ;i <= m;i ++ )
20 { b[i1][j]每一次都应该初始化,因为空间是循环利用的
21 b[i1][i-1] = - 2000000000 ; //已改动
22 for (j = i;j <= n;j ++ )——————从数字数不少于分段数的地方开始
23 {
24 if (i == j)f[j] = sum[j];
25 else
26 {
27 f[j] = f[j - 1 ] + a[j];
28 if (f[j] < b[ 1 - i1][j - 1 ] + a[j])f[j] = b[ 1 - i1][j - 1 ] + a[j];
29 }
30 b[i1][j] = b[i1][j - 1 ];
31 if (b[i1][j] < f[j])b[i1][j] = f[j];
32 }
33 i1 = 1 - i1;—————i1总是0-1交替变化
34 }
35 for (i = m;i <= n;i ++)——————此时f[i]中存储前i个数分成m段得到的最大和
36 if (ans < f[i])ans = f[i];
37 printf( " %d\n " ,ans);
38 }
39 return 0 ;
40 }
此题难在两点:1.找到状态转移方程;2.优化程序。
状态转移方程:f[i][j]=max{f[i-1][j],max{f[i-1][k]}}+a[j]; (i-1=<k<=j-1)
f[i][j]代表将前j个数分成i段可以得到的最大和,并且这i段中必须包括a[j].它可以由两个状态转移到:a[j]与其他数一起包含
到第j段中,所得到的最大和(f[i-1][j]+a[j]);a[j]独立成段,与前i-1个数分成的j-1段得到的最大和(max{f[i-1][k]}+a[j])。
由于问题所涉及的规模实在太大,所以必须用滚动数组存储状态。同时设置sum[]数组,其中存储当i=j即分段数等于数字数时的
状态值,也就是所有j个数的和。主要代码见下:
1 i1 = 1 ;
2 ans =- ( 1 << 30 );
3 for (i = 1 ;i <= m;i ++ )
4 {
5 for (j = i;j <= n;j ++ )
6 {
7 if (i == j)f[i1][j] = sum[j];
8 else
9 {
10 f[i1][j] = f[i1][j - 1 ] + a[j];
11 for(k=i-1;k<=j-1;k++ )
12 if(f[i1][j]<f[1-i1][k]+a[j])f[i1][j]=f[1-i1][k]+ a[j];
13 }
14 }
15 i1 = 1 - i1;
16 }
17 j = m % 2 ;
18 for (i = m;i <= n;i ++ )
19 if (ans < f[j][i])ans = f[j][i];
20 printf( " %d\n " ,ans);
红色代码区就是上述转移状态的第二个状态。这个代码的效率不高,很大程度上是因为这个循环。优化办法如下:
设置一个数组b[i][j],它是滚动式的,用来存储前j个数划为i段得到的最大和,但不一定包括a[j]。这样转移方程就可以变
为这样: f[i][j]=max{f[i-1][j],b[i-1][j-1]}+a[j];
b[i][j]=max{b[i][j-1],f[i][j]}
这样的话,在程序运行是就包含了两个转移方程,b[i][j]的值由两种状态转移可得:前j个数分为i段选a[j]得到的最大和(f[i][j]);
不选a[j]得到的最大和,即前j-1个数分为i段得到的最大和(b[i][j-1])。此时f[i][j]还可以省去一维,变为f[j].最终优
化后的程序见最上部,还有若干细节问题在程序旁会有注释。