HDU 1024 MAX sum plus plus 动态规划

题目链接: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): 12181    Accepted Submission(s): 4024


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 3 

2 6 -1 4 -2 3 -2 3

 
Sample Output

6

8

 

=======================================================================================

=====================================我是分割线=========================================

=======================================================================================

 

这题的思路是看了别人的博客,好久才想明白!

博客 地址:http://blog.sina.com.cn/s/blog_677a3eb30100jxqa.html

思路:
dp[i][j]表示前j个元素分成i段的最优解,同时这个最优解是由a[j]元素结束的。
转移方程是dp[i][j] = max { f[i][j-1]+a[j],max(f[i-1][k]+a[j],  (i-1<=k<j)  } (i<=j<=n-m+i)
其中j的上下界的确定比较麻烦。现在分别解释上界和下界:
上界:dp[i][j]中,如果j=i-1,意思就是在前面i-1个元素中分成i段,这个是不可能实现的。
下界:如果m=n=4,这时dp[2][4]求出来了,意思是前面的四个元素分成了两段,当是还有两段要分,
           所以求出这个是没有意义的。当然求出来也不会影响结果,只是这样时间复杂度就提高了。

这是其中一个特例的状态转移表 m=4,n=6 ,-1 4 -2 3 -2 3

图 1​

 

 

没有填的说明不用算。
很显然dp[i][i]=dp[i-1][i-1]+a[i],
所以对角线上面有:
 
图 2

 

现在演示一下转移过程。如何求下图的框中的元素的值

 

图 3


 

由下面涂色的元素的最大值加上a[3]=-2求的如下图

 

图 4

 


 

 

最大的是4,所以4+(-2)=2;框中填2;
假如框中的元素是dp[i][j],画圈的元素表示的是,左边那个是dp[i][j-1],上面的几个是dp[i-1][k](i-1<=k<j)} ,这个就是上面的转移方程的表格表示法。

 

以上是大牛做的解说介,下面是我自己的看法:

我们可以这么理解,把这n个数求最大的 m个子串 和, 那么我们可以把最后的 m 个数分离出来,这最后的m个数肯定需要加入计算范围,题目要求子串不能互相包含,则可以把m个数依次加入计算范围,加入第 i 个数就计算了 i 个子串和,直到 i == m,这样我们就减少了计算,节约了时间开销!

比如测试数据的第二组:

               a[1]     a[2]      a[3]    a[4]     a[5]     a[6]

2 6     -1    4    -2    3    -2    3    中间的是dp状态

i = 0    0    0     0    0     0    0    

i = 1   -1    4     2    5     3    6    这里的dp[1][6]其实可以不用计算,这里只是为了方便说明

i = 2   -1    3     2    7     5    8    

 

i == 0 : 初始状态

i == 1 : 保存的是到a[j]为止的一段子串和(根据的是上一状态dp[i-1][0]到dp[i-1][j-1]中的最大值 + a[j]与dp[i][j-1] + a[j]两者中 的最大值)

i == 2 : 保存的是到a[j]为止的两段子串和(根据同上)

 

解题代码:

 

G++代码
 1 // File Name: Max Sum Plus Plus 1024.cpp
 2 // Author: sheng
 3 // Created Time: 2013年04月30日 星期二 16时46分15秒
 4 
 5 #include <stdio.h>
 6 #include <string.h>
 7 #include <iostream>
 8 using namespace  std;
 9 #define Max(a, b) (a) > (b) ? (a) : (b)
10 typedef __int64 LL;
11 
12 const int max_n = 1e6 + 5;
13 LL s[max_n];
14 LL dp[2][max_n];//由于矩阵可能很大所以采用滚动数组比较实用
15 
16 int main()
17 {
18     int n, m;
19     int i, j;
20     while (~scanf ("%d%d", &m, &n))
21     {
22         for (i = 1; i <= n; i ++)
23         {
24             scanf ("%I64d", &s[i]);
25             dp[0][i] = dp[1][i] = 0;//初始化dp数组
26         }
27         int pos = 1;     //初始使用的滚动数组中的一行
28         for (i = 1; i <= m; i ++)     //这里从一段子串和开始直到m段子串和
29         {    // 1
30             dp[pos][i] = dp[pos^1][i-1] + s[i];
31             LL max = dp[pos^1][i-1];
32             for (j = i + 1; j <= n - m + i; j ++)
33             {
34                 max = Max ( max, dp[pos^1][j-1] );
35                 dp[pos][j] = Max ( max + s[j], dp[pos][j-1] + s[j]);
36             }
37              // 2
38             ///////1~2已经在思路中说明就不多说了
39             pos ^= 1; //滚动数组实现滚动的条件
40         }
41         pos ^= 1;  //当i > m 时要记得返回上一状态
42         LL ans = -1e10;
43         for (i = m; i <= n; i++) //这个循环为了找到最大的 m段子串和
44         {
45             ans = Max (ans, dp[pos][i]);
46         }
47         printf ("%I64d\n", ans);
48     }
49     return 0;
50 }

 

原文地址:https://www.cnblogs.com/shengshouzhaixing/p/3053563.html