Question

给一个长度为N的数组,一个长为K的滑动窗体从最左端移至最右端,你只能看到窗口中的K个数,每次窗体向右移动一位,你的任务是找出窗体在各个位置时的最大值和最小值。


Sample input

8 3
1 3 -1 -3 5 3 6 7

Sample output

-1 -3 -3 -3 3 3
3 3 5 5 6 7


Answer:

第一眼看这题好简单啊
直接建立一颗线段树,然后区间查询(easy!),交上去发现 MLE了40%;😟
考虑更优的解法:
单调队列:就像单调栈一样,保证队列里的下标代表的元素一直是递增或递减。
我们用这个队首维护当前窗口的答案
那么当滑动这个窗口到了Ai时,Ai肯定是要进队列的,那再他进队列之前,就可以把队列里所有比他大的元素(即从队尾开始往前判断)全都出队了(因为这些数字是肯定不会再次对答案造成 贡献了)
然后再将所有不再当前窗口的队首出队。一直维护就是了

这题其实是一道很基础的单调队列模板题,目的应该是为了让大家熟悉一下这种思想。
可以用来优化一些种类的DP。

#include <bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
#define pb push_back
#define fi firse
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef vector<int> VI;
const int maxn = 1e6 + 6;
const LL inf = 0x3f3f3f3f;
const int mod = 1e9+7;
LL qp(LL x,LL y){LL ans=1;x%=mod;while(y){if(y&1) ans=ans*x%mod;x=x*x%mod;y>>=1;}return ans;}

//head
int n,k,a[maxn],q[maxn];

int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int l=1,r=0;
    for(int i=1;i<=n;i++){
        while(l<=r&&q[l]+k<=i) l++;
        while(l<=r&&a[q[r]]>a[i]) r--;
        q[++r]=i;
        if(i>=k) printf("%d ",a[q[l]]);
    }
    printf("\n");
    l=1;r=0;
    for(int i=1;i<=n;i++){
        while(l<=r&&q[l]+k<=i) l++;
        while(l<=r&&a[q[r]]<a[i]) r--;
        q[++r]=i;
        if(i>=k) printf("%d ",a[q[l]]);
    }
    return 0;
}