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 \to 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\n", f[n]);
    }
    return 0;
}
posted @ 2020-02-05 22:57  quanjun  阅读(157)  评论(0编辑  收藏  举报