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(i x, j x)( 1 ≤ x ≤ m) instead. ^_^
 

 

Input
Each test case will begin with two integers m and n, followed by n integers S 1, S 2, S 3 ... S n.
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].最终优

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

 

你可能感兴趣的:(HDU)