Max Sum Plus Plus |
---|
题意: 从n个数中选取m个区段,使得和最大。今天脑子一直不在线,以至于看了题解也不明白是为什么。终于在发呆了亿会之后,终于回过神来看懂了。
题解: n的范围在1e6,而m的范围直接不告诉你了,所以想开二维的DP去做的话,后果你懂的。直接步入正题,怎么用一维的数组来写。
- 定义状态dp[i]:题目中有一个条件,选取m个区段,如果只是单纯求最大的区段和,就简单得多,但是我们要刚好控制是m个区段,同时又是个一维数组。所以定义状态dp[i]表示前i个数中选取了i之后所能达到的最大和,之所以这样定义状态,使为了控制选取m个区段,i>=m时保证选取了至少m个区段。
- 状态转移:为了使dp[i]最大,我们需要从dp[1]~dp[i-1]中选取一个最大值加上a[i](明确一点,dp[i]使选取了第i个数之后所能达到的最大值,并不代表在dp[1 ~ i]之间就一定最大。 )因此状态转移方程大致可以表示为:
dp[i] = max{dp[1], dp[2], ...,dp[i - 1]} + a[i]
。 - 定义pre[i]:作用无非就是记录下前i个dp中的最大值而已,不至于查找都重新遍历一遍。
- 循环计算m次dp:dp毕竟只是一维的,跑一次不过只能确定一个区段的最大和而已,如果时m个区段,那我们就跑m次循环咯。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define endl "\n"
#define dbg(x...) \
do { \
cout << #x << " -> "; \
err(x); \
} while (0)
void err() { cout << endl;}
template <class T, class... Ts>
void err(const T& arg, const Ts&... args) {
cout << arg << ' ';
err(args...);
}
inline int read(){
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int N = 1e6 + 10;
int n, m;
int maxdp;
int a[N];
int dp[N];
int pre[N];
void run() {
for(int i = 1; i <= n; i++){
a[i] = read();
dp[i] = pre[i] = 0;
}
for(int i = 1; i <= m; i++) {
maxdp = -inf;
for(int j = i; j <= n; j++) {
dp[j] = max(dp[j - 1], pre[j - 1]) + a[j];
pre[j - 1] = maxdp; //注意更新的是pre[j - 1],记录的是在第i次循环时,前j个dp的最大值,需要在i+1之后才被调用
maxdp = max(maxdp, dp[j]);
//可以试着在这里更新pre[j] = maxdp。结果会出错,读者需要自己思考琢磨一下
}
}
printf("%d\n", maxdp);
}
int main() {
while(~scanf("%d%d", &m, &n)) run();
return 0;
}