题目:给你一个序列 S1, S2, S3, S4 ... Sx, ... Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). 我们定义
sum(i, j) = Si + ... + Sj (1 ≤ i ≤ j ≤ n).现在给你一个 m(8>m>0&&m<n)你的任务是计算
sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) ;我们规定他是不相交的。
sum(i, j) = Si + ... + Sj (1 ≤ i ≤ j ≤ n).现在给你一个 m(8>m>0&&m<n)你的任务是计算
sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) ;我们规定他是不相交的。
请输出m段最大和,比如:m = 2,n = 6 ,{-1 4 -2 3 -2 4} 它的结果是 9;
要用到两个DP;
dp[i][j]表示分成i段,最后一段以j结尾的最大子串和。
dp2[i][j]表示分成 i段,到第j个数字的最大字串和(分法不一定包括的j,注意和上面区分)
答案就是dp2[m][n];
转移方程为dp[i][j] = max(dp[i][j - 1], dp2[i - 1][j - 1]) + z[j];//两种 情况,要么是接在前一个后面,要么是单独作为一段的开头
dp2[i][j] = max(dp[i][j], dp2[i][j - 1]);//考虑最新的分法是否超过了最大值
考虑使用滚动数组,dp的话只使用当前层的前一个,所以完全没有问题。dp2的话使用了当前层的前一个和上一层的前一个,所以我们在更新当前层的时候不能马上覆盖到上一层的,因为在下一层时要用到。所以考虑使用一个变量来暂时保存当前层;
转移方程变为:dp[j] = max(dp[j - 1], dp2[j - 1]) + z[j];
dp2[j - 1] = ans;//上一层的用完才覆盖
ans = max(dp[j], ans);//暂时不覆盖当前层,下一层要用
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <queue>
#include <vector>
using namespace std;
#define MAXN 275005
#define MOD 1000000
#define LL long long int
int T, n, m, x;
int z[1000005];
LL dp[1000005];
LL dp2[1000005];
LL ans;
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d %d", &m, &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &z[i]);
}
memset(dp, 0, sizeof(dp));
memset(dp2, 0, sizeof(dp2));
dp[0] = dp2[0] = 0;
for (int i = 1; i <= m; ++i)
{
ans = -0x7f7f7f7f;
for (int j = i; j <= n; ++j)
{
dp[j] = max(dp[j - 1], dp2[j - 1]) + z[j];
dp2[j - 1] = ans;
ans = max(dp[j], ans);
}
}
printf("%lld\n", ans);
}
}