题目描述
给定 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 子段和。
样例数据 1
输入
3 1
1 2 3
输出
6
样例数据 2
输入
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; }