Max Sum Plus Plus (动态规划) HDU1024
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1024
(http://www.fjutacm.com/Problem.jsp?pid=1375)
题意:长度为n的序列里,m段不相关区间的最大和
思路:我们先要确定一个东西,就是状态,这里我用dp[i][j]表示前j个数在取a[j]情况下分i段的最大和;
那么我们为了找规律,可以先来一发Excel,就以样例为例子:
然后我们可以发现其实红圈里的8是状态dp[2][6](i=2, j=6),那么我们可以想想这个位置怎么推导,很明显,他可以选择和分i-1块的最大值相加,得到的i块可能是最大,或者他也可以直接和同样分i块的j-1的位置相加,这样就相当于不断开,得到最大。那么也就是他只有两种选择,第一个是dp[i][j-1],第二个是max(dp[i-1][i-1]~dp[i-1][j-1]),也就是dp[i][i~n]只和dp[i-1][i-1~n]这一行的状态有关,和别的无关。那么我们就可以用滚动数组保存;但是如果你找max(dp[i-1][i-1]~dp[i-1][j-1])的时候用的是for查找的话,那就凉凉了,因为那样复杂度就是O(n^3),也就是我们要用一个maxn来记住之前的最大值,然后每次更新记录;具体看代码。
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 const int N= 1001000; 6 const long long INF=0x3f3f3f3f3f3f3f3f; 7 long long dp[2][N], maxn; 8 int a[N]; 9 int main( ){ 10 int m, n, t; 11 while(~scanf("%d%d", &m, &n)){ 12 t=1;///用来滚动数组 13 for(int i=1; i<=n; ++i) 14 dp[0][i]=-INF; 15 for(int i=1; i<=n; ++i) 16 scanf("%d", &a[i]); 17 for(int i=1; i<=m; ++i, t=1-t){///t=1-t就是在循环滚动 18 dp[t][i]=dp[1-t][i-1]+a[i];///对角线的值其实就是前n项和啦!! 19 maxn=dp[1-t][i-1];///别把这个忘了 20 for(int j=i+1; j<=n; ++j){ 21 maxn=max(maxn, dp[1-t][j-1]);///maxn更新记录max(dp[i-1][i-1]~dp[i-1][j-1]) 22 dp[t][j]=max(dp[t][j-1], maxn)+a[j];///状态的转移步骤 23 } 24 } 25 t=1-t;///最后i>m时的那一个++i, t=1-t的影响要转过来 26 maxn=-INF; 27 /** 28 注意,dp[i][j]是表示前j个数在取a[j]情况下分i段的最大和; 29 也就是dp[m%2][n不一定是最优解,因为可能不加a[n]还更大; 30 **/ 31 for(int i=m; i<=n; ++i) 32 maxn=max(maxn, dp[t][i]); 33 printf("%I64d\n", maxn); 34 } 35 return 0; 36 }