HDU-1024 Max Sum Plus Plus
HDU-1024 Max Sum Plus Plus
问题描述
m段最大子段和
给一串数\(S1,S2,...,Sx,...,Sn(1<=x<=n<=1000000,-32768<=S_x<=32767)\)
定义\(sum(i,j)=S_i+\dots+S_j(1<=i<=j<=n)\)
给一个\(m(m>0)\),要求找出\(m\)对\(i\)和\(j\),使得\(sum(i_1,j_1)+\dots+sum(i_m,j_m)\)最大,输出这个最大值
限制\(i_x<=i_y<=j_x\)和\(i_x<=j_y<=j_x\)是不允许的
分析
① \(i_x<=i_y<=j_x\)和\(i_x<=j_y<=j_x\)
段与段不相交,包括端点也不重合。如果是第一次见,确实要想一下才知道是什么意思。
在草稿纸上多画一下。
② 问题定义与最优子结构性质
m段子段和是在最大子段和基础上的扩展。
子段:连续,n->n-1的分治。
起点、终点两边都动不好找关系,固定一边,将前面整体打包考虑。
m段子段和要求段与段不相交,下一段的开头必须大于前一段的结尾。
然而答案如何得来,具体如何分的细节不用关注,所以仍然不需要两边都动,不需要关心开头,只需要抓住结尾,问题定义时在取值范围上体现不相交的限制条件。
\(dp[m][j]\),第一维表示分成了几段,第二维是固定的最右元素。
动态规划的本质之一是所有方案都要做一遍,所有路径都要走一遍。
最右元素固定,也就是一定要选。一定要选有两种情况,一种情况是单独作为一段,另一种情况是和前面的连成一段(连续)。
-
单独作为一段,则前面必定有m-1段,这m-1段的最右元素不固定,但是因为有不相交的限制,因此最右元素必定有范围限制。写出递归方程\(dp[m][j] = dp[m-1][k] + a[j]\),其中\(1<=k<=j-1\)。
-
和前面的连成一段,则前面必定有了m段,且最右元素必定为\(j-1\)(连续),才接得起来。
写出递归方程\(dp[m][j] = dp[m][j-1] + a[j]\).
运算为加法运算,最大加最大等于最大,其中一项不是最大,则和就不是最大,得到的解就不是最优解,该问题符合最优子结构性质。
③ 实现
画表格,观察运算顺序,用循环实现。
| | | | | |
|⚪|⚪|⚪| | |
| | |⚪ |🔺 | |
| | | | | |
从上到下,从左到右计算
④ 优化
\(dp[m][j] = max(dp[m-1][k] + a[j], dp[m][j-1] + a[j])\),其中\(1<=k<=j-1\).
注意,不需要再和自己取max,是两种情况中选一种情况,如果再套一层\(max(dp[m][j])\),在滚动数组的时候很容易出错。
题目给的数据范围是1000000,如果套3层循环在k处暴力枚举,必定超时。由于m的范围没有给,可以认为和n的范围一样,也就是每个数自成一段,开一个大数组依次填表也必定超空间。
空间优化——滚动数组
从以上表格可以看到用到的只是前后两行。%2技巧。
注意%2每个地方都要%2,不然会RE.
TLE时间优化——数组记录
根据最优子结构性质,只需要上一行\(dp[m-1][k]\)的最大值,因此记录\(last\_max[m][j]\).
继续优化——二维变一维
由于有数组记录最大值,因此不需要两行%2,可以进一步优化变成一维。
只是要注意在滚动的时候,\(last\_max[j-1]\)不能先更新,否则用的不是上一轮的,而是这一轮的。
隐含限制,分段段数不会超过数字个数。因此对于\(j<m\)的情况可以跳过,但是由于一维滚动,所以要把值设好,这样才不会出现用到上上轮的值的情况。
限制与初始化
这个题开long long会超时,而且用二维的dp容易WA,初始化要想好,很容易出错,可以把第一步作为例子来想。能优化则优化,能一维则一维。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN = 1000010;
int M, n;
int dp[MAXN] = {0};
int last_max[MAXN] = {0};
int a[MAXN];
int main() {
while (scanf("%d%d", &M, &n) == 2) {
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
dp[0] = 0; last_max[0] = 0;
for (int j = 1; j <= n; j++) {
dp[j] = 0;
last_max[j] = 0;
}
int temp = 0;
for (int m = 1; m <= M; m++) {
for (int j = m; j <= n; j++) {
dp[j] = max(dp[j - 1] + a[j], last_max[j - 1] + a[j]);
if (j == m) {
last_max[j - 1] = -0x3F3F3F3F; // 问题就出在这里
} else {
last_max[j - 1] = max(last_max[j - 2], dp[j - 1]);
}
}
}
printf("%d\n", max(last_max[n-1], dp[n]));
}
return 0;
}