题意: 给定长度为
的序列
,从中选取
个子段,求子段和的最大值。
题解:
表示将前
个元素分成
段可以获得的最大
段和,其中这里必然包括第
个元素。
状态转移为
- 表示第 个元素划分到第 段,并将前 个元素分成 段的最大和
- ,表示将第 个元素划分到第 段,且将前 个元素划分成 段可以获得的最大和。
这里的时间复杂度和空间复杂度必然爆炸,考虑优化。
- 首先, 只和前一个状态 有关,故可以用滚动数组优化。
- 其次,枚举 是 的时间复杂度,考虑动态更新 的最大值,这里每一层的 也只对下一层有影响,故也可以滚动数组优化。
- 所以这里记: 表示前 个元素(必然包括 ),构成最多 个子段可以得到的最大和, 表示前 个元素中可以得到的最大和。
代码:
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int m, n;
int s[N];
int pre[N], f[N];
int main()
{
while(~scanf("%d%d", &m, &n)) {
for(int i = 1; i <= n; i++) pre[i] = 0, f[i] = 0;
for(int i = 1; i <= n; i++) scanf("%d", &s[i]);
int temp = 0;
for(int i = 1; i <= m; i++) {
temp = -0x7fffffff; //temp动态更新前j-1元素分成i-1段的最大值
for(int j = i; j <= n; j++) {
f[j] = max(f[j - 1], pre[j - 1]) + s[j]; //f[j-1]表示将j划到i段,pre[j-1]表示将j划到i-1段
pre[j - 1] = temp; //更新为第i+1层,这里的temp是j-1时的
temp = max(temp, f[j]);
}
}
//由于f[n]表示必然选第n个元素,但实际答案未必。
printf("%d\n", temp);
}
}
注意: 为了保证分成 个子段至少要有 个元素,枚举元素数要从子段数开始。