hdu1024---最大m段不重合子序列和:策略优化

http://acm.hdu.edu.cn/showproblem.php?pid=1024(到这里提交)

Max Sum Plus Plus

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4977    Accepted Submission(s): 1627


Problem Description
Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.

Given a consecutive number sequence S1, S2, S3, S4 ... Sx, ... Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). We define a function sum(i, j) = Si + ... + Sj (1 ≤ i ≤ j ≤ n).

Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) maximal (ix ≤ iy ≤ jx or ix ≤ jy ≤ jx is not allowed).

But I`m lazy, I don't want to write a special-judge module, so you don't have to output m pairs of i and j, just output the maximal summation of sum(ix, jx)(1 ≤ x ≤ m) instead. ^_^
 

 

Input
Each test case will begin with two integers m and n, followed by n integers S1, S2, S3 ... Sn.
Process to the end of file.
 

 

Output
Output the maximal summation described above in one line.
Sample Input
1 3 1 2 32 6 -1 4 -2 3 -2 3
Sample Output
68
Hint
Huge 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].最终优

化后的程序见最上部,还有若干细节问题在程序旁会有注释。

 

posted @ 2010-07-24 20:57  Danty  阅读(1083)  评论(5编辑  收藏  举报