洛谷:P2032 扫描(彻底搞懂单调队列)


传送门:P2032 扫描 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P2032

题目描述

4644aeed2c1a4bad8a3069b1fd852555.png

用暴力解法时间复杂度为O(k*n)显然是不能通过有的示例的,我们可以考虑优化,有什么算法可以直接得到k区间的最大值呢?

这个时候就引入了单调队列,我们知道单调队列可以维护一个区间的最大值和最小值,既然是单调队列,那我们是要用单调递增的队列,还是用单调递减的队列呢?

下面可以找到答案。

求解步骤和思路可以参考求m区间内的最小值滑动窗口

洛谷:P1440 求m区间内的最小值_´悠 子ᴗ`-_-╭☞ꪗꪖꪑ的博客-CSDN博客用单调队列来求解求m区间内的最小值https://blog.csdn.net/qq_66500237/article/details/127797778?utm_source=app&app_version=5.5.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

或:

洛谷:P1886 滑动窗口 /【模板】单调队列题解_´悠 子ᴗ`-_-╭☞ꪗꪖꪑ的博客-CSDN博客用单调队列求解移动窗口https://blog.csdn.net/qq_66500237/article/details/127803829

对于这题的本质就是滑动窗口求每个k区间的最大值,我们用一个单调队列去维护队首为最大值,也就是说这个单调队列为一个相对于队首来说的单调递减的队列,队首就是最大。要注意的是,每次入队都要保证队首在k区间内,还要保证队列的单调性。

单调递减队列有:(我这里的递减是相对于队首的递减)

for(int i=1;i<=n;i++){
while(q[front]<=i-k&&front<=rear)front++;//保证队首在k区间内
		while(front<=rear&&a[q[rear]]<=a[i])rear--;//保证队列的单调性
		q[++rear]=i;        //入队
}

front 和 rear 分别为头指针和尾指针。

如 案例中的 n=5,k=3 {1,5,3,4,2}

队列中情况队列操作
1队列为空,1入队
55>1,1出队,5入队
5 33<5,3入队
5 44>3,3出队,4<5,4入队
4 25超出了k区间,5出队,2<4,2入队

 从队列的整个操作过程我们可以知道,队首就是我们要找的最大值,而且也维护了队首在k区间内,这就是单调队列求k区间的最大值的好处。

代码:

#include<iostream>
using namespace std;
int q[2000005],a[2000005],n,k,front=1,rear=0;
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		while(q[front]<=i-k&&front<=rear)front++;//保证队首在k区间内
		while(front<=rear&&a[q[rear]]<=a[i])rear--;//保证队列的单调性
		q[++rear]=i;        //入队
		if(i>=k)cout<<a[q[front]]<<"\n";
	}
	return 0;
}

6f294312490e4db088929eff797825c6.png

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值