原题链接:https://www.luogu.com.cn/problem/P1886
滑动窗口 /【模板】单调队列
题目描述
有一个长为 n n n 的序列 a a a,以及一个大小为 k k k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
例如:
The array is [ 1 , 3 , − 1 , − 3 , 5 , 3 , 6 , 7 ] [1,3,-1,-3,5,3,6,7] [1,3,−1,−3,5,3,6,7], and k = 3 k = 3 k=3。
输入格式
输入一共有两行,第一行有两个正整数 n , k n,k n,k。 第二行 n n n 个整数,表示序列 a a a
输出格式
输出共两行,第一行为每次窗口滑动的最小值
第二行为每次窗口滑动的最大值
输入输出样例
输入 #1
8 3
1 3 -1 -3 5 3 6 7
输出 #1
-1 -3 -3 -3 3 3
3 3 5 5 6 7
说明/提示
【数据范围】
对于
50
%
50\%
50% 的数据,
1
≤
n
≤
1
0
5
1 \le n \le 10^5
1≤n≤105 ;
对于
100
%
100\%
100% 的数据,
1
≤
k
≤
n
≤
1
0
6
1\le k \le n \le 10^6
1≤k≤n≤106 ,
a
i
∈
[
−
2
31
,
2
31
)
a_i \in [-2^{31},2^{31})
ai∈[−231,231)。
题解
👴这道模板题之前居然只有
70
70
70分,一看竟是尝试用
S
T
\mathcal{ST}
ST表偷鸡
M
L
E
\mathcal{MLE}
MLE了,那么正好来水一篇博客复习一下单调队列吧。
队列跟栈的区别在于队列不仅可以从队尾出队,还可以从队首出队,同时单调队列还要求队列中的元素保持单调。
那么单调队列如何解决滑动窗口问题呢?以维护最小值为例,当我们从队尾向队列中加入元素时,这个队列中所有大于等于当前加入元素的元素都无意义了,因为从队尾加进去的元素在窗框中停留的时间严格长于前面的元素,所以当该元素存在时,窗框覆盖区域中的最小值只可能是该元素。所以,每当我们想要从队尾加入新元素时,之前队列里大于等于新元素的元素都从队尾出队了,自然使整个队列变得单调了。同时,因为窗框有长度限制,当队首元素与当前元素的距离大于窗框长度时,应该使其从队首出队。
由此,单调队列中就是当前窗框覆盖元素的一个单调字串,每次询问时输出此时的队首元素即可。
维护最大值同理。
代码
队列中储存的是元素的位置编号。
#include<bits/stdc++.h>
using namespace std;
const int M=1e6+5;
int n,k,que[M],x[M],head,tail;
void in()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)scanf("%d",&x[i]);
}
void ac()
{
que[head=tail=1]=1;if(k==1)printf("%d ",x[1]);
for(int i=2,a;i<=n;++i)
{
for(;x[que[tail]]>=x[i]&&tail>=head;--tail);
que[++tail]=i;
for(;i-que[head]+1>k&&head<=tail;++head);
if(i>=k)printf("%d ",x[que[head]]);
}
putchar(10);
que[head=tail=1]=1;if(k==1)printf("%d ",x[1]);
for(int i=2,a;i<=n;++i)
{
for(;x[que[tail]]<=x[i]&&tail>=head;--tail);
que[++tail]=i;
for(;i-que[head]+1>k&&head<=tail;++head);
if(i>=k)printf("%d ",x[que[head]]);
}
putchar(10);
}
int main()
{
in(),ac();
system("pause");
}