【模板】单调队列 滑动窗口最值

Luogu P1886 滑动队列/单调队列

有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

以求最小值为例
f[i]表示以i结尾的窗口中的最小值
f[i]=min(a[j]),i-k+1<=j<=i
暴力算法O(n^2)
for:i=1~n
   for:j=i-k+1~i
       f[i]=min(a[j) 


单调队列:队尾进队出队,队头出队(维护子序列的单调性)

5 7 8 ——> 5 7 ——> 5 ——> 5 6 ——> 5 6 8

1.队尾出队的条件:队列不空且新元素更优,队中旧元素队尾出队
2.每个元素必然从队尾进队一次
3.队头出队的条件:队头元素滑出了窗口
注意:队列中存储元素的下标,方便判断队头出队 


//维护窗口最小值
int h=1,t=0;                             //清空队列
for(int i=1;i<=n;i++) {                  //枚举序列
    while(h<=t&&a[q[t]]>=a[i]) t--;       //队尾出队(队列不空且新元素更优)  min(a[i])
    q[++t]=i;                            //队尾入队(存储下标 方便判断队头出队)  j<=i
    if(q[h]<i-k+1) h++;                  //队头出队(队头元素滑出窗口)   i-k+1<=j
    if(i>=k) cout<<a[q[h]]<<" ";         //使用最值
}

每个元素最多进队和出队各一次,时间复杂度为O(n)

#include <bits/stdc++.h>

using namespace std;

const int N=1e6+10;
int a[N],q[N];
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[i];
    //维护窗口最小值
    int h=1,t=0;
    for(int i=1;i<=n;i++) {
        while(h<=t&&a[q[t]]>=a[i]) t--;
        q[++t]=i;
        if(q[h]<i-k+1) h++;
        if(i>=k) cout<<a[q[h]]<<" ";
    }
    cout<<'\n';
    //维护窗口最大值
    h=1,t=0;
    for(int i=1;i<=n;i++) {
        while(h<=t&&a[q[t]]<=a[i]) t--;
        q[++t]=i;
        if(q[h]<i-k+1) h++;
        if(i>=k) cout<<a[q[h]]<<" ";
    }
    return 0;
}

维护最小值就是维护窗口内的升序子序列

维护最大值就是维护窗口内的降序子序列

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

July.19th

感谢各位对我的支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值