zoukankan      html  css  js  c++  java
  • BZOJ 3831 Little Bird 题解 单调队列优化DP

    题目链接:https://www.vijos.org/d/newbzoj/p/590c9934d3d8a13210993a72

    题目大意:

    有一排n棵树,第i棵树的高度是Di。
    MHY要从第一棵树到第n棵树去找他的妹子玩。
    如果MHY在第i棵树,那么他可以跳到第i+1,i+2,...,i+k棵树。
    如果MHY跳到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。
    为了有体力和妹子玩,MHY要最小化劳累值。

    解题思路:

    设第 (i) 棵树高 (a[i]),设状态 (f[i]) 表示从第 (1) 棵树走到第 (i) 棵树的最小劳累值,则

    [f[i] = min_{j=i-k o i-1} (f[j] + a[j]>a[i]?1:0) ]

    朴素方法是 (O( n cdot q cdot k )) 的,会超时。所以考虑维护一个单调队列来优化DP。

    这个单调队列需要满足如下性质:

    假设单调队列里面靠前的那个坐标是 (i),靠后的那个坐标是 (j),则必然满足如下条件中的一个:

    • (f[i] = f[j])(a[i] > a[j])(花费相同的情况下,数越高的优先级越高)
    • (f[i] < f[j])(花费小情况下我可以多花一块钱)

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1000010;
    int n, q, k, a[maxn], f[maxn];
    deque<int> que;
    bool check(int i, int j) {
        return f[i] == f[j] && a[i] > a[j] || f[i] < f[j];
    }
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++) scanf("%d", a+i);
        scanf("%d", &q);
        while (q --) {
            scanf("%d", &k);
            while (!que.empty()) que.pop_back();
            f[1] = 0;
            que.push_back(1);
            for (int i = 2; i <= n; i ++) {
                int j = que.front();
                f[i] = f[j] + (a[j] > a[i] ? 0 : 1);
                while (!que.empty() && !check(que.back(), i)) que.pop_back();
                que.push_back(i);
                if (que.front()+k < i+1) que.pop_front();
            }
            printf("%d
    ", f[n]);
        }
        return 0;
    }
    
  • 相关阅读:
    Mathematica 计算矩阵的伴随矩阵
    教你如何在word中像LaTex那样打出漂亮的数学公式
    中国科学院大学2016年硕转博考试试题
    161024解答
    161023解答
    161020-1解答
    关于查询扩展版ESI高被引论文的说明
    [Tex学习笔记]让项目编号从4开始
    [Tex学习]WinEdit 常用软件快捷键
    最著名的数学家一般也是最著名的力学家
  • 原文地址:https://www.cnblogs.com/quanjun/p/12267160.html
Copyright © 2011-2022 走看看