题目链接:
P1886 滑动窗口 /【模板】单调队列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
说明:
参考:第九章:单调栈与单调队列_单调栈和单调队列-CSDN博客
练习一下单调队列模板,后面有些题好做一些。
步骤(来自上面的参考文章):
- step1 : 在队列不为空的条件下,判断第一个元素是否在窗口内,不在就出队。
- step2 : 在队列不为空的条件下,若队尾元素大于(取决于求最大还是最小,这里求最小)等于目标元素则删除,直到队尾元素小于目标元素
- step3 :目标元素进队。
- step4:已经形成了窗口,相应窗口保存最值
注意:
1.以求最大值为例,弹出窗口尾部的小于将要入队的元素的部分是用while,因为可能不止当前尾部一个小于将要入队的元素,要全部弹出保持单调。
2.i-k+1其实对应的就是窗口的编号,i-k+1>=0的时候,i每次滑动一个单位,就会产生一个窗口。
3.记得判断h<=t,需要保证队伍不为空 才能对队列元素做查询和操作
4.队列存储下标,更加方便
5.不要忘了新元素进队
代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N=1e6+10;
int ans=0;
int q[N];
int mx[N],mn[N];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n,k;
cin>>n>>k;
vector<int> a(n,0);
for(int i=0;i<n;i++){
cin>>a[i];
}
// 当tail>=head时,说明有元素。而一开始队列为空
int h=0,t=-1;
//求最大
for(int i=0;i<n;i++){
//需要保证队伍不为空
//队头元素已经不在窗口内,弹出
if(h<=t&&q[h]<i-k+1){
h++;
}
//这个地方是while ,为了保持单调,要把窗口内 比当前要入队的元素 小的全部弹出
//尾元素比将要入队的 小,那么滑动窗口框着这个尾元素的时候也会框着他后面这个比他大的数,注定不能是窗口里的最大值
//所以出队
while(h<=t&&a[q[t]]<a[i]){
t--;
}
q[++t]=i;
if(i-k+1>=0)
mx[i-k+1]=a[q[h]];
}
//最小
h=0,t=-1;
for(int i=0;i<n;i++){
if(h<=t&&q[h]<i-k+1){
h++;
}
//这个地方是while
while(h<=t&&a[q[t]]>a[i]){
t--;
}
q[++t]=i;
if(i-k+1>=0)
mn[i-k+1]=a[q[h]];
}
for(int i=0;i<n-k+1;i++){
cout<<mn[i]<<" ";
}
cout<<endl;
for(int i=0;i<n-k+1;i++){
cout<<mx[i]<<" ";
}
return 0;
}