HDU_1024.MaxSumPlusPlus(基础DP + 滚动数组优化讲解)

  这道题打破了我常规的做题思路,因为这是我刚开始训练DP,感觉这道题目好晕眼呀,emm其实就是感觉自己是真的菜......

  为什么说打破了我的做题思路呢,因为我平时看题解都是在已经AC或者完全不懂的情况下看了题解用的知识点,然后再自学知识点完成题目,结果这次.......我是真的鸡...

  好了言归正传,看了刘汝佳大佬的线性dp和滚动数组类型的内容,也有一定的了解,下面我会解释什么情况下可以滚动数组降维减少空间复杂度,那么又该如何降维呢?

  

  本题大意:给定一个数字m和n个数字,让你求出这n个数字分为m份的和的最大值,具体分割要求见题目。

  

  本题思路:很显然就是dp了,由于题目要求是求将n个数字分割成m部分的和的最大值,那么从dp[ m ][ n ]开始考虑,dp[ m ][ n ]只与第n个数字的状态有关,我们可以先试着写一下状态转移方程

  dp[ i ][ j ] = max(dp[ i ][j - 1], dp[i - 1][ k ]) + a[ j ])( k >= 1 && k <= j - 1), 显然这个式子中有两部分,即当第 j 项直接加到j - 1项后面时为dp[ i ][ j ] = dp[ i ][j - 1] + a[ j ],当不直接加入时则第 j 项作为第 i 个子序列的开头,则dp[ i ][ j ] =  dp[i - 1][ k ] + a[ j ]。我们选择将第 i 个子序列与 第 i - 1个子序列中的最大值合并,我们将每次得到的最优值更新并保存即可。

  题目只给了32MB的空间,二维数组是不可能二维数组的了,顶多是滚动数组优化一下这样子......

  参考代码:

 1 //dp[i][j] 表示第j个数字在第i个序列时的最优值
 2 #include <iostream>
 3 #include <cstring>
 4 using namespace std;
 5 
 6 const int N = 1e6 + 5, INF = 0x3f3f3f3f;
 7 int a[N], dk[N], dp[N];
 8 
 9 int main () {
10     int m, n, maxn;
11     while(cin >> m >> n) {
12         for(int i = 1; i <= n; i ++)
13             cin >> a[i];
14         memset(dk, 0, sizeof dk);
15         memset(dp, 0, sizeof dp);
16         for(int i = 1; i <= m; i ++) {
17             maxn = -INF;//初始化
18             for(int j = i; j <= n; j ++) {
19                 cout << dk[j - 1] << '	';
20                 if(i == j) dp[j] = dk[j - 1] + a[j];//第i个元素只能作为第i个子序列的第一个元素
21                 else dp[j] = max(dp[j - 1] + a[j], dk[j - 1] + a[j]);//选择两种决策中的最大值(直接接到j - 1后面或者以j再作为新的开头)
22                 dk[j - 1] = maxn;//maxn保存的是第j - 1位置的最优值
23                 if(maxn < dp[j]) maxn = dp[j];//如果遇到更优的值则更新
24                 //dk[j] = maxn;//那么我们为什么不这样呢,如果我们更新第j项那么意味着我们在下次计算dp[j + 1]时用到的dk[j] = dp[j] ??? 这明显与我们的状态转移方程不符合
25                 //因为我们更新dp[j]时用到的是dk[j - 1],那我们只需要更新这个值即可。
26             }
27             cout << endl;
28         }
29         cout << maxn << endl;
30     }
31     return 0;
32 }
View Code

   很容易可以看出,第 i 个子序列的最优值只与第i - 1层他对应的最优值有关,则对于dp[ i ][ j ] = max(dp[ i ][j - 1], dp[i - 1][ k ]) + a[ j ]),我们可以看出,只需要在遍历的时候依次访问他的子序列长度即可,并不需要保存每层的最优值,因为长度为 i 的子序列的最优解可以由第i - 1层的最优解得来,因此我们只需要保存一层的最优解,然后在计算下一层的最优解时更新保存的值即可......。

  我们可以用一个一维数组来保存我们在第i - 1层中遇到的最大值。切记是第i - 1层。

原文地址:https://www.cnblogs.com/bianjunting/p/10612462.html