• 单调队列优化动态规划


    先来看这道题:

    USACO 2011 Open Gold Mowing the Lawn 修剪草坪

    After winning the annual town competition for best lawn a year ago,
    Farmer John has grown lazy; he has not mowed the lawn since then
    and thus his lawn has become unruly. However, the competition is
    once again coming soon, and FJ would like to get his lawn into
    tiptop shape so that he can claim the title.

    Unfortunately, FJ has realized that his lawn is so unkempt that he
    will need to get some of his N (1 <= N <= 100,000) cows, who are
    lined up in a row and conveniently numbered 1..N, to help him. Some
    cows are more efficient than others at mowing the lawn; cow i has
    efficiency E_i (0 <= E_i <= 1,000,000,000).

    FJ has noticed that cows near each other in line often know each
    other well; he has also discovered that if he chooses more than K
    (1 <= K <= N) consecutive (adjacent) cows to help him, they will
    ignore the lawn and start a party instead. Thus, FJ needs you to
    assist him: determine the largest total cow efficiency FJ can obtain
    without choosing more than K consecutive cows.

    显然这是一道动态规划题目,我们先简化题目。题意是这样的:

    FJ 有 N(1≤N≤100000) 只排成一排的奶牛,编号为 1...N。每只奶牛的效率是不同的, 奶牛 i 的效率为 Ei(0≤Ei≤1000000000)。靠近的奶牛们很熟悉,因此,如果 FJ 安排超过 K(1≤K≤N) 只连续的奶牛,那么,这些奶牛就会罢工去开派对。现在 FJ 需要你帮助计算可以得到的最大效率。

    如果我们用 dp[i] 来表示前i头奶牛的最大效率,那么我们可以写出这样的转移方程:

    dp[i]=max​{dp[j−1]+sum[i]−sum[j]} (i−K≤j≤i)

    这样的转移方程的时间复杂度是O(NK),观察这道题的数据量,相当于O(N^2),显然是不行的。

    仔细观察,实际上对于 dp[i] 来说,我们需要找到一个决策 j(i−K≤j≤i) 使得 dp[j−1]−sum[j] 最大化。再看,不难发现,i和j都是单调递增的,这让我们联想到以前做过的Sliding Windows,窗户的两边也是单调的对吧。所以这道题我们其实也可以用单调队列来优化。我们用这个单调队列来维护这个下标j的位置。

     1 #include <stdio.h>
     2 
     3 typedef long long LL;
     4 const int maxn = 100010;
     5 LL dp[maxn], sum[maxn];
     6 int que[maxn], E[maxn];
     7 int head, tail;
     8 LL max(LL a, LL b) {
     9     return a > b? a : b;
    10 }
    11 void add(int j) {
    12     while (head < tail && dp[j - 1] - sum[j] >= (que[tail - 1] > 0 ? dp[que[tail - 1] - 1] : 0) - sum[que[tail - 1]]) {
    13         --tail;
    14     }
    15     que[tail++] = j;
    16 }
    17 void del(int j) {
    18     if (head < tail && que[head] == j) {
    19         ++head;
    20     }
    21 }
    22 int main() {
    23     int n, k;
    24     scanf("%d %d", &n, &k);
    25     sum[0] = 0;
    26     for (int i = 1; i <= n; ++i) {
    27         scanf("%d", &E[i]);
    28         sum[i] = sum[i - 1] + E[i];
    29     }
    30     dp[0] = 0;
    31     que[tail++] = 0;
    32     for (int i = 1; i <= n; ++i) {
    33         add(i);
    34         del(i - k - 1);
    35         int j = que[head];
    36         dp[i] = (j > 0 ? dp[j - 1] : 0) + sum[i] - sum[j];
    37     }
    38     LL ans = max(dp[n], dp[n - 1]);
    39     printf("%lld
    ", ans);
    40     return 0;
    41 }
  • 相关阅读:
    Think 框架漏洞利用
    提权篇之简单介绍和exp利用过程
    webshell提权20种思路
    黑吃黑,大神实操带你破解菠菜平台
    知道这20个正则表达式,能让你少写1,000行代码
    Spring与Quartz的整合实现定时任务调度
    关于MySQL的wait_timeout连接超时问题报错解决方案
    SpringMVC 400 Bad Request 问题
    java 细说String
    最详细的Log4j使用教程
  • 原文地址:https://www.cnblogs.com/OIerPrime/p/9745970.html
Copyright © 2020-2023  润新知