P1886 【模板】单调队列 / 滑动窗口
单调队列中存储着具有规律的答案 根据答案的性质 按照答案的规律创建一个答案队列 只有有可能成为答案的候选项才有可能进入队列 这样的队列往往具有一一定的单调性质 、
题目描述
有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个窗口从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最小值和最大值。
例如,对于序列 [1,3,−1,−3,5,3,6,7] 以及 k=3,有如下过程:
窗口位置[1 3 -1] -3 5 3 6 7 1 [3 -1 -3] 5 3 6 7 1 3 [-1 -3 5] 3 6 7 1 3 -1 [-3 5 3] 6 7 1 3 -1 -3 [5 3 6] 7 1 3 -1 -3 5 [3 6 7]最小值−1−3−3−333最大值335567
输入格式
输入一共有两行,第一行有两个正整数 n,k;
第二行有 n 个整数,表示序列 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% 的数据,1≤n≤105;
对于 100% 的数据,1≤k≤n≤106,ai∈[−231,231)。
对于这道题 一个区间上的最大值和最小值
我们可以维护一个最大值最小值队列 维护这一段的可能答案 对于最大值如果出现了一个值大于当前队列里的其他值 那么比他小的值就一定不会成为答案 那么我们可以直接弹出队列 将有可能称为答案 的候选项加入队列 也就是最大值递减 入队列 这个队列的最左端 也就是队首就是最大的 也就是这个区间的最大值
最小值同理 我们保证最小值队列单调递增 如果不符合那就不断弹出元素直到符合这一特征
对于两个队列我们要维护他们元素的有效性 删除过期的元素
cpp
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int mina[N],mint[N],maxa[N],maxt[N],maxl,maxr=1,minl=0,minr=1;
int ansmin[N],ansmax[N];
int a;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a;
while(maxt[maxl]<=i-k&&maxl<maxr)maxl++;
while(mint[minl]<=i-k&&minl<minr)minl++;
while(maxa[maxr-1]<=a&&maxr>maxl)maxr--;
while(mina[minr-1]>=a&&minr>minl)minr--;
maxa[maxr]=mina[minr]=a;
maxt[maxr++]=mint[minr++]=i;
ansmax[i]=maxa[maxl];
ansmin[i]=mina[minl];
}
for(int i=k;i<=n;i++){
cout<<ansmin[i]<<' ';
}cout<<endl;
for(int i=k;i<=n;i++){
cout<<ansmax[i]<<' ';
}cout<<endl;
return 0;
}