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