给定 n 个数求这 n 个数划分成互不相交的 m 段的最大 m 子段和。
给出一段整数序列 A1,A2,A3,A4,...,Ax,...,An ,其中 1≤x≤n≤1,000,000, -32768≤Sx≤32767。
我们定义一种函数 sum(i,j)=Ai + ... + Aj (1≤i≤j≤n,且Ai~Aj 是连续的数)。
现在,我们得到一个正整数 m(1≤x≤m≤30),你的工作是寻找 m 对 i 与 j。这 m 对 i 和 j 满足以下条件:
sum(i1,j1)+sum(i2,j2)+sum(i3,j3)+...+sum(im,jm)在这个序列中最大。
注意:任意两区间[ix,jx]和[iy,jy]的交集均为空集。
请你求出:最大的 sum(i1,j1)+sum(i2,j2)+sum(i3,j3)+...+sum(im,jm)是多少?
第一行两个整数 n,m 。
第二行由空格隔开的 n 个整数,即 A1~An 。
请输出我们定义的:sum(i1,j1)+sum(i2,j2)+sum(i3,j3)+...+sum(im,jm)的最大值,即最大的 m 子段和。
输入
6 2
-1 4 -2 3 -2 3
输出
8
题目分析
形如“求将x,分成y份的最*值”问题,通常dp都为 f[j][i],表示将前j个元素分成i份的最*值。
这道题也一样:f[j][i]如上所述,对于新加入的元素,有两种决策:
- 加到上一次的最后一段。
- 独立成段。
转移方程就为:$f[j][i] = max(f[j - 1][i] + val[i], max_{k = 1}^{j - 1}\{f[j - 1][k]\} + val[i])$
稍微计算一下,会发现这样的空间已经报表。接下来就是滚动数组登场了。
可以发现,转移方程中的j这一维总是从上一次的j-1转移,而i不变,而且f[j - 1][k]也只需要上一次的最大值即可,那么就可以使用滚动数组进行如下优化:
1. 去掉i这一维, 并把i这一维提到外循环->保证j-1到j都是i这一维。 数组只需要f[N]
2.用mx[j - 1]来表示$max_{k = 1}^{j - 1}\{f[j - 1][k]\} $, 每次内循环(j)更新, 以供下一个外循环(i)使用。
于是转移变为:
for(int i = 1; i <= m; i++){
ll tmp = -oo;
for(int j = i; j <= n; j++){
f[j] = max(f[j - 1] + val[j], mx[j - 1] + val[j]);
tmp = max(tmp, f[j - 1]);
mx[j - 1] = tmp;
}
}
code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
const int N = 1e6 + 5, oo = 0x7fffffff;
int n, m;
typedef long long ll;
ll f[N], mx[N], val[N];
inline ll read(){
ll i = 0, f = 1; char ch = getchar();
for(; (ch < ‘0‘ || ch > ‘9‘) && ch != ‘-‘; ch = getchar());
if(ch == ‘-‘) f = -1, ch = getchar();
for(; ch >= ‘0‘ && ch <= ‘9‘; ch = getchar())
i = (i << 3) + (i << 1) + (ch - ‘0‘);
return i * f;
}
inline void wr(ll x){
if(x < 0) putchar(‘-‘), x = -x;
if(x > 9) wr(x / 10);
putchar(x%10+‘0‘);
}
int main(){
n = read(), m = read();
for(int i = 1; i <= n; i++) val[i] = read();
for(int i = 1; i <= m; i++){
ll tmp = -oo;
for(int j = i; j <= n; j++){
f[j] = max(f[j - 1] + val[j], mx[j - 1] + val[j]);
tmp = max(tmp, f[j - 1]);
mx[j - 1] = tmp;
}
}
ll ans = -oo;
for(int i = m; i <= n; i++)
ans = max(ans, f[i]);
wr(ans), putchar(‘\n‘);
return 0;
}