LGP1886 滑动窗口/【模板】单调队列
题意
给定长(n)的序列(a),以及大小为(k)的窗口。现在窗口从左向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。(kle n le 10^6,a_iin[-2^{31},2^{31}))
样例
8 3
1 3 -1 -3 5 3 6 7
(第一行最小值,第二行最大值)
-1 -3 -3 -3 3 3
3 3 5 5 6 7
题解
以样例为例。用(q)表示单调队列,(p)表示其所对应的在原来列表里的序号。
- 原来队列中没有一个元素,直接令1进队。此时q={1},p={1}.
- 下面基于这样一个思想:如果把3放进去,如果后面两个数(k=3)都比他大,就有可能成为最小的。此时q={1,3},p={1,2}.
- 下面出现了-1,队尾元素3比-1大,意味着只要-1进队,那么3一定不可能成为最小值【原因是框3的同时一定会框住-1】,所以3从队尾弹出。进行同样的思考,1也从对尾出队。最终-1进队,此时q={-1},p={3}.
- 出现-3,同上面分析,-1出队,-3进队,q={-3},p={4}。
- 出现5,因为5>-3,根据第二条分析,5还是有希望的,所以5进队。此时q={-3,5},p={4,5}
- 出现3,3与队尾的5比较,3<5,按照第3条的分析,5从队尾出队,3再与-3比较,同第二条,3进队。此时q={-3,3},p={4,6}
- 出现6.6与3比较,因为3<6,所以不必出队。由于3以前元素都<3,所以不必再比较,6进队,因为-3此时已经在滑动窗口之外,所以-3从队首出队。此时q={3,6},p={6,7}
- 出现7.队尾元素6小于7,7进队。此时q={3,6,7},p={6,7,8}
code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn=1000001;
int n,k,a[maxn];
int q[maxn],head,tail,p[maxn];
int read(){
int ans=0,op=1;
char c;
c=getchar();
for(;(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-') op=-1,c=getchar();
for(;(c>='0'&&c<='9');c=getchar()) ans*=10,ans+=c^48;
return ans*op;
}
void init(){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++) a[i]=read();
}
void findmax(){
head=1,tail=0;
for(int i=1;i<=n;i++){
while(head<=tail&&q[tail]<=a[i]) tail--;
q[++tail]=a[i];
p[tail]=i;
while(p[head]<=i-k) head++;
if(i>=k) printf("%d ",q[head]);
}
printf("
");
}
void findmin(){
head=1,tail=0;
for(int i=1;i<=n;i++){
while(head<=tail&&q[tail]>=a[i]) tail--;
//只要队列里有元素,并且队尾元素比要处理的a[i]大,那么表示队尾元素已经不可能出场
//所以出队,知道队尾元素小于待处理的a[i],满足单调
q[++tail]=a[i];//处理值入队
p[tail]=i;//同时存下编号
while(p[head]<=i-k) head++;//如果队首元素已经不在窗口内,出队
if(i>=k) printf("%d ",q[head]);
}
printf("
");
}
int main(){
init();
findmin();
findmax();
return 0;
}