[算法学习笔记] 单调队列

当一个选手比你小还比你强,你就可以退役了。
-----单调队列

简介

单调队列一般用于维护动态区间内的极值,它可以做到线性的复杂度下求出所有动态区间的极值。

它的原理在上文引用部分已经提到,每次队列中只维护可能成为区间极值的元素,具体地,例如求区间最小值,若队列中有的数比新增加的数大,则可以直接弹出,因为它不可能成为现在乃至以后的区间最大值。新增的数一定要入队,因为显然它可能成为后面的区间极值(因为在队头会不断弹出)

单调队列一般用双端队列实现,在队头进行“退役”也就是超出滑动区间范围的数的弹出,在队尾进行不可能成为区间极值的数弹出以及新增数。

另外,由于我们每次比新增数大/小的数会弹出,所以队列元素满足单调性,因此输出的时候输出队头即可。

单调队列一般不会单独考察,主要用于优化,例如单调队列优化dp。

单调队列基本内容就这些?是不是很简单!

例题&参考板子

经典:Luogu P1886 滑动窗口/模板单调队列

我的板子:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <deque>
#define N 1000010
using namespace std;
deque <int> q,q1;
int n,k;
int a[N];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        if(!q1.empty()&&i-q1.front() >= k) q1.pop_front(); //如果队尾下标-队头下标也就是队列长度超出,则队头元素“退役”
        while(!q1.empty()&&a[q1.back()] > a[i]) //弹出不可能是区间极值的元素
        {
            q1.pop_back();
        }
        q1.push_back(i); //新增元素入队尾
        if(i>=k) cout<<a[q1.front()]<<" "; //动态输出队头,因为满足单调性,队列元素单调
    }
    cout<<endl;
    for(int i=1;i<=n;i++)
    {
        if(!q.empty()&&i-q.front() >= k) q.pop_front();
        while(!q.empty()&&a[q.back()] < a[i])
        {
            q.pop_back();
        }
        q.push_back(i);
        if(i>=k) cout<<a[q.front()]<<" ";
    }
}
posted @ 2023-08-08 23:57  SXqwq  阅读(17)  评论(0编辑  收藏  举报