ybtoj 单调队列课堂过关 例题3luogu P3572 [POI2014]耗费体力 & PTA-Little Bird

Posted SSL_ZZL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ybtoj 单调队列课堂过关 例题3luogu P3572 [POI2014]耗费体力 & PTA-Little Bird相关的知识,希望对你有一定的参考价值。

【例题3】耗费体力 & 【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龙珠

ybtoj 单调队列课堂过关 例题2POJ 1821DP粉刷木板 & Fence

ybtoj线段树课堂过关例题1求区间和

ybtoj背包问题课堂过关DP例题3宝物筛选

Ybtoj 树形DP课堂过关例题3最长距离

luogu P4513ybtoj线段树课堂过关例题3小白逛公园