今天我们来聊聊单调队列
单调单调,就代表这种队列具有单调性
而单调性又分为单调递增与单调递减
举个栗子:
1 4 5 6 7 8 10
这就是一个单调递增的序列
而
1 4 6 5 7 8 10
就不是
而这和我们的“单调队列”又有什么关系呢?
单调队列的作用就是将区间最小或者最大值维护在队首
那么我们要怎么维护呢?
给同学们思考1年
大家解放自己的脑洞,试想一下如果一个队列从队首到队尾单调递减,那么队首不就是区间最大值了吗?
没错!这就是“单调队列”的一个核心思想
于是乎,构建单调队列的方法也非常明显了
比如
1 7 5 4 8 2 6
我们怎样创建一个区间最大值单调队列呢?
我们需要保证队列单调递减
工程开始:
i=1,a[i]=1
,队首为0,0<1,清空队列,1入队,队列为 ( 1 )
i=2,a[i]=7
,队首为1,1<7,清空队列,7入队,队列为 ( 7 )
i=3,a[i]=5
,队首为7,7>5,队尾为7,7>5,5入队,队列为 ( 7 , 5 )
i=4,a[i]=4
,队首为7,7>4,队尾为5,5>4,4入队,队列为 ( 7 , 5 , 4 )
i=5,a[i]=8
,队首为7,7<8,清空队列,8入队,队列为 ( 8 )
i=6,a[i]=2
,队首为8,8>2,队尾为8,8>2,2入队,队列为 ( 8 , 2 )
i=7,a[i]=6
,队首为8,8>6,队尾为2,2<6,2出队,6入队,队列为 ( 8 , 6 )
工程结束。
大家可以发现,在每一个过程中,我们都对队首队尾进行了判断,保证了队列的单调递减,成功维护了队首为区间最大值
例题 P1886 滑动窗口
原题链接
0、首先读题,发现这题要维护区间的最大值与最小值,判断算法为单调队列
1、创建 4 个队列maxn,minx,x,y,分别用于存储区间最大最小值及序号,推荐使用 STL 的 deque
2、从数组的第一个元素开始遍历,先对前 m-1 个元素创建单调队列,再继续创建单调队列
3、如果当前元素比 maxn 队首元素要大,说明队中的所有元素都比当前元素小,清空队列,当前元素入队
从队尾开始将比当前元素小的元素全部弹出
如果队首的序号超出本区间,弹出队首
minx 队列的做法与 maxn 相似,大家可以试着举一反三
代码仅供参考:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000005],ans1[1000005],ans2[1000005];
deque <int> maxn,minx,x,y;//注意用deque
inline int read()
{
int x=0,w=1;
char ch=0;
while(ch<'0' || ch>'9')
{
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') x=(x << 3)+(x << 1)+ch-'0',ch=getchar();
return x*w;
}
//读优
int main()
{
n=read();
m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<m;i++)
{
while(!maxn.empty() && a[i]<=maxn.back()) maxn.pop_back(),x.pop_back();
while(!minx.empty() && a[i]>=minx.back()) minx.pop_back(),y.pop_back();
maxn.push_back(a[i]);
x.push_back(i);
minx.push_back(a[i]);
y.push_back(i);
}//重要,先对前 m-1 个元素创建单调队列
for(int i=m;i<=n;i++)
{
while(!maxn.empty() && a[i]<=maxn.back()) maxn.pop_back(),x.pop_back();
while(!minx.empty() && a[i]>=minx.back()) minx.pop_back(),y.pop_back();
maxn.push_back(a[i]);
x.push_back(i);
minx.push_back(a[i]);
y.push_back(i);
while(!maxn.empty() && i-x.front()>=m) maxn.pop_front(),x.pop_front();
while(!minx.empty() && i-y.front()>=m) minx.pop_front(),y.pop_front();//单调队列的维护
ans1[i]=maxn.front();
ans2[i]=minx.front();//记录答案
}
for(int i=m;i<=n;i++) printf("%d ",ans1[i]);
printf("\n");
for(int i=m;i<=n;i++) printf("%d ",ans2[i]);//输出即可
return 0;
}
原创 By Venus
写的不好大佬轻喷