Codeforces 1077F2 Pictures with Kittens (hard version)——单调队列优化dp

题意:

从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

考虑单调队列优化,发现循环的时候外层j内层i就可以实现,对于dp[i][j],维护dp[i-k~i][j-1]的单调递增队列(队首元素最小)就可以了

#include 
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;
}

 

你可能感兴趣的:(数据结构,动态规划)