单调队列

单调队列

顾名思义,是一个用双端队列维护的具有单调性质的队列,队列内部元素可以是从大到小也可以是从小到大,可以在\(O(n)\)的时间内提取出一个长度为\(n\)的数组中每一段连续固定长度的子段的最大值或者最小值

实现起来也很容易,假如说我们现在要求出数组\(a\)每个长度为k的最大值(题目传送

操作一
从第一个数开始扫,设当前的数为\(a_i\),当队列里非空的时候,取出队列里最后的数(当前区块最小值),比较这个数与\(a_i\)的大小关系,如果队列最后面的数比\(a_i\)小,就将其弹出,重复执行直到没有比\(a_i\)小的或者队列空了

这样我们就能保证这个单调性,但是还有一个问题,就是怎么控制我现在单调队列维护的是这个长度为\(k\)的区块的最大值呢?

操作二
这也是为什么我们要用双端队列,我们发现,队头的数一定是最先入队列的,每次加进来新的数之前(执行完操作一之后),我们也要检查一下,(队列非空的时候)队头的数距离现在的数(也就是下标差)是否小于等于k,否则这个队头就是不合法,不应该在当前区间的,就将它弹出,检查完后就可以将当前数入队尾了

代码实现(单调不减和单调不增两种)

#include<bits/stdc++.h>
using namespace std;
int n,k;
int a[1000010];
int minn[1000010],maxx[1000010];
int main(){
	ios::sync_with_stdio(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	deque<int> q1;//最大 
	deque<int> q2;//最小 
	for(int i=1;i<=n;i++){
		while(!q1.empty()&&a[q1.back()]<a[i]){//如果队尾的值比当前小就弹出
			q1.pop_back();//操作一
		}
		q1.push_back(i);//先进行操作二也可以,不过如果先进行操作2就需要我上文说的检查队列是否非空
		if(i-q1.front()>=k) q1.pop_front();//操作二
		if(i>=k) maxx[i-k+1]=a[q1.front()];//提取当前区间值
	}
	for(int i=1;i<=n;i++){
		while(!q2.empty()&&a[q2.back()]>a[i]){
			q2.pop_back();
		}
		q2.push_back(i);
		if(i-q2.front()>=k) q2.pop_front();
		if(i>=k) minn[i-k+1]=a[q2.front()];
	}
	for(int i=1;i<=n-k+1;i++) cout<<minn[i]<<' ';
	cout<<'\n';
	for(int i=1;i<=n-k+1;i++) cout<<maxx[i]<<' ';
}
posted @ 2024-11-29 10:15  hapihapi  阅读(33)  评论(0)    收藏  举报