CF1077F2.Pictures with Kittens (hard version) 题解 单调队列优化dp

题目链接:https://codeforces.com/problemset/problem/1077/F2

题目大意:

在长度为 \(n\) 的序列里面选择恰好 \(x\) 个元素,使得所有长度 \(\ge k\) 的连续子序列里面都至少包含一个选择的元素。求 \(x\) 个选择的元素的最大和。

解题思路:

动态规划。定义状态 \(f_{i,j}\) 表示表示选择 \(a_i\) 作为第 \(j\) 个数的情况下的最大数字和。

则状态转移方程为 \(f_{i,j} = \max \{ f_{i-k,j-1}, f_{i-k+1,j-1}, \ldots, f_{i-1,j-1} \}\)

但是直接这么写的话时间复杂度是 \(O(n \cdot x \cdot k)\),可以使用单调队列优化,时间复杂度讲到 \(O(n \cdot x)\)

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5005;
deque<int> que;
int n, k, x, a[maxn];
long long f[maxn][maxn], ans = -1;
int main() {
    scanf("%d%d%d", &n, &k, &x);
    for (int i = 1; i <= n; i ++)
        scanf("%d", a+i);
    memset(f, -1, sizeof(f));
    for (int i = 1; i <= k; i ++)
        f[i][1] = a[i];
    for (int j = 2; j <= x; j ++) {
        que.clear();
        for (int i = j; i <= min(n, j*k); i ++) {
            if (f[i-1][j-1] != -1) {
                while (!que.empty() && f[que.back()][j-1] <= f[i-1][j-1])
                    que.pop_back();
                que.push_back(i-1);
            }
            assert(!que.empty());
            if (i - que.front() > k) que.pop_front();
            assert(!que.empty());
            f[i][j] = max(f[i][j], f[que.front()][j-1] + a[i]);
        }
    }
    for (int i = 0; i < k; i ++)
        ans = max(ans, f[n-i][x]);
    printf("%lld\n", ans);
    return 0;
}

滚动数组优化

上面的代码空间复杂度是 \(O(n \cdot x)\),稍微有点大,所以课时使用滚动数组优化,代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5005;
deque<int> que;
int n, k, x, a[maxn];
long long f[maxn][2], ans = -1;
int main() {
    scanf("%d%d%d", &n, &k, &x);
    for (int i = 1; i <= n; i ++)
        scanf("%d", a+i);
    memset(f, -1, sizeof(f));
    for (int i = 1; i <= k; i ++)
        f[i][1] = a[i];
    for (int j = 2; j <= x; j ++) {
        int J = j%2;
        que.clear();
        for (int i = 0; i <= n; i ++) f[i][J] = -1;
        for (int i = j; i <= min(n, j*k); i ++) {
            if (f[i-1][J^1] != -1) {
                while (!que.empty() && f[que.back()][J^1] <= f[i-1][J^1])
                    que.pop_back();
                que.push_back(i-1);
            }
            assert(!que.empty());
            if (i - que.front() > k) que.pop_front();
            assert(!que.empty());
            f[i][J] = max(f[i][J], f[que.front()][J^1] + a[i]);
        }
    }
    for (int i = 0; i < k; i ++)
        ans = max(ans, f[n-i][x%2]);
    printf("%lld\n", ans);
    return 0;
}
posted @ 2021-10-05 20:56  quanjun  阅读(38)  评论(0编辑  收藏  举报