Codeforces 1077F2 Pictures with Kittens (hard version)——单调队列优化dp
原创
©著作权归作者所有:来自51CTO博客作者软糖酱八号机的原创作品,请联系作者获取转载授权,否则将追究法律责任
题意:
从n个数中选x个,要求任意连续k个必须有数被选中,问最后x个数的和最大是多少
1<=n,k<=5000, 1<=x<=n
思路:
设dp[i][j]为选中a[i]且选了j个时的最大和,则dp[i][j] = max{dp[t][j-1]|i-k<=t<i}+a[i]
考虑单调队列优化,发现循环的时候外层j内层i就可以实现,对于dp[i][j],维护dp[i-k~i][j-1]的单调递增队列(队首元素最小)就可以了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5010;
typedef long long ll;
const ll INF = 1e14;
int n, K, x, a[maxn];
ll dp[maxn][maxn], que[maxn];
int main() {
scanf("%d%d%d", &n, &K, &x);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= x; j++) {
dp[i][j] = -INF;
}
}
dp[0][0] = 0;
for (int j = 1; j <= x; j++) {
que[0] = 0;
int l = 0, r = 1;
for (int i = 1; i <= n; i++) {
while (l < r && que[l] < i-K) l++;
dp[i][j] = dp[que[l]][j-1] + a[i];
while (l < r && dp[que[r-1]][j-1] <= dp[i][j-1]) r--;
que[r++] = i;
}
}
long long ans = -INF;
for (int i = n-K+1; i <= n; i++) {
ans = max(ans, dp[i][x]);
}
if (ans < 0) printf("-1\n");
else printf("%I64d\n", ans);
return 0;
}