题目链接: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
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. ^_^
Process to the end of file.
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
6
8
=======================================================================================
=====================================我是分割线=========================================
=======================================================================================
这题的思路是看了别人的博客,好久才想明白!
博客 地址:http://blog.sina.com.cn/s/blog_677a3eb30100jxqa.html
这是其中一个特例的状态转移表 m=4,n=6 ,-1 4 -2 3 -2 3
图 1

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

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

以上是大牛做的解说介,下面是我自己的看法:
我们可以这么理解,把这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]为止的两段子串和(根据同上)
解题代码:

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 }