题目
给定一行n个非负整数a[1]..a[n]。现在你可以选择其中若干个数,但不能有超过k个连续的数字被选择。你的任务是使得选出的数字的和最大。
分析
顺便写一下dp思考方法吧,考虑从第一位开始递推处理,在第i位的时候,需要在i-k位(取i-k+1到i)到i(不取i)中找一个点不取,设这个点为j,这一段连续的就是j+1到i,那么f[i] = max{f[j-1] + sum[j+1, i]} (i-k<=j<=i),运用前缀和简化一下就是f[i] = max{f[j-1]-sum[j]+sum[i]}+f[i];发现max里面的值只与j有关,所以可以用单调队列优化转移。
需注意:1、单调队列别写错了== 2、注意何时出队,何时入队 3、注意边界 4、选择方便转移的方程格式,比如可以用d[i]记录f[j-1]-sum[j],以方便判断单调队列的出队。
代码
#include <cstdio>
#include <iostream>
using namespace std;
long long f[100200];
int n, k;
long long a[100200], b[100200], d[100200];
long long qq[100200], head, tail = 1;
const int INF = (1<<31)-1;
int put_in(int j) {
d[j] = f[j-1] - b[j];
if(j >= k+1 && d[j-k-1] == qq[head]) head++;
while(tail > head && d[j] > qq[tail-1]) tail--;
qq[tail++] = d[j];
}
int main() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[i] = b[i-1] + a[i];
}
d[0] = 0;
for(int i = 1; i <= n; i++) {
put_in(i);
f[i] = qq[head] + b[i];
}
cout << f[n] << endl;
return 0;
}
ps:这题调了来回三个小时,智障属性完全暴露,忘记考虑i不取这个条件,人傻还要多做题啊。