ybtoj 单调队列课堂过关 例题3luogu P3572 [POI2014]耗费体力 & PTA-Little Bird
Posted SSL_ZZL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ybtoj 单调队列课堂过关 例题3luogu P3572 [POI2014]耗费体力 & PTA-Little Bird相关的知识,希望对你有一定的参考价值。
Link
ybtoj【单调队列课堂过关】【例题3】耗费体力
luogu 【P3572】[POI2014]PTA-Little Bird
题面//因为不知道侵不侵权所以就是题面是私密的,有账号的直接看转送门就可了
题目大意
给出一排树,高度为
A
1
,
A
2
.
.
.
A
n
A_1,A_2...A_n
A1,A2...An,可以从第 i 棵树跳到第 (i + 1) ~ (i + k) 棵树
如果从矮树跳到高树,耗费体力1;如果从高树跳到矮树,不耗费体力
给出m个查询,每次给出k,问从第1棵跳到 第n 棵树的最小耗费体力
样例
样例输入
9
4 6 3 6 3 7 2 6 5
2
2
5
样例输出
2
1
解题思路
考虑DP
设 f[i] 为从第1棵树跳到第 i 棵树的最小耗费
f[1] = 0;
for(int i = 2; i <= n; i++) {
f[i] = INF;
for(int j = max(1, i - k); j < i; j++)
if(a[j] <= a[i])f[i] = min(f[i], f[j] + 1);
else f[i] = min(f[i], f[j]);
}
这么狂暴 n ^ 2 肯定不行
考虑单调队列优化
发现每次只会利用到 f[i-k] ~ f[i-1] 最小的那个
于是可以写一个单调递增队列
Code
#include <iostream>
#include <cstdio>
#define N 1000100
using namespace std;
int n, m, k, l, r;
int a[N], q[N], f[N];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
scanf("%d", &m);
while(m --) {
scanf("%d", &k);
l = 1, r = 1, q[1] = 1, f[1] = 0;
for(int i = 2; i <= n; i ++) {
while(l <= r && i - q[l] > k) l ++; //超出可跳范围
if(a[q[l]] <= a[i]) f[i] = f[q[l]] + 1; //正常状态转移
else f[i] = f[q[l]];
while(l <= r && (f[i] < f[q[r]] || (f[i] == f[q[r]] && a[i] > a[q[r]]))) r --;
//维护单调递增,如果 f 相同,尽量使 a 更高,这样下一步不耗费体力的情况就更多
q[++r] = i;
}
printf("%d\\n", f[n]);
}
}
以上是关于ybtoj 单调队列课堂过关 例题3luogu P3572 [POI2014]耗费体力 & PTA-Little Bird的主要内容,如果未能解决你的问题,请参考以下文章
ybtoj 二叉堆课堂过关 例题3luogu 月赛 P5462指针龙珠游戏 & X龙珠