【ybtoj 单调队列课堂过关 例题3】【luogu 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]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值