单调队列定义:

  其实单调队列就是一种队列内的元素有单调性的队列,因为其单调性所以经常会被用来维护区间最值或者降低DP的维数已达到降维来减少空间及时间的目的。

  单调队列的一般应用:

    1.维护区间最值

    2.优化DP

例题引入:

  求m区间内的最小值:https://www.luogu.org/problemnew/show/P1440

  一个含有n项的数列(n<=2000000),求出每一项前的m个数到它这个区间内的最小值。若前面的数不足m项则从第1个数开始,若前面没有数则输出0。

 

例题解答:

   首先看到题目可以很快想到O(NM),对于2*10^6这样的数据无疑要TLE的;

   接下来考虑用单调队列,因为每一个答案只与当前下标的前m个有关,所以可以用单调队列维护前m的个最小值,

   考虑如何实现该维护的过程??

   显然当前下标X的m个以前的元素(即下标小于X-M+1的元素)肯定对答案没有贡献,所以可以将其从单调队列中删除。

   对于两个元素A,B,下标分别为a,b,如果有A>=B&&a<b那么B留在队列里肯定优于A,因此可以将A删除。

   维护队首:如果队首已经是当前元素的m个之前,将head++,弹出队首元素

   维护队尾:比较q[tail]与当前元素的大小,若当前元素更优tail++,弹出队尾元素,直到可以满足队列单调性后加入当前元素。

   考虑单调队列的时间复杂度:由于每一个元素只会进队和出队一次,所以为O(N)。

   一般建议用数组模拟单调队列进行操作,而不用系统自带的容器,因为系统自带容器不易调试且可能有爆空间的危险。

 

代码实现:

  

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define INF 0x3f3f3f3f
#define ll long long
#define maxn 2000009
#define maxm
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')    f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
int n,m,k,tot,head,tail;
int a[maxn],q[maxn];
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    head=1,tail=0;//起始位置为1 因为插入是q[++tail]所以要初始化为0
    for(int i=1;i<=n;i++)//每次队首的元素就是当前的答案 
    {
        printf("%d\n",a[q[head]]);
        while(i-q[head]+1>m&&head<=tail)//维护队首 
            head++;
        while(a[i]<a[q[tail]]&&head<=tail)//维护队尾 
            tail--;
        q[++tail]=i;
    }
//    fclose(stdin);
//    fclose(stdout);
    return 0;
}

 

习题报告:

   滑动窗口:https://www.luogu.org/problemnew/show/P1886

   解题思路: 此题与例题相同,只是所要求的是最大值和最小值,只需要做两遍单调队列即可   

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define maxn 1000009
#define maxm
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
int q[maxn],a[maxn];
int n,m,k,ans,tot,head,tail; 

void Ask_MIN()
{
    head=1,tail=0;
    for(int i=1;i<=n;i++)
    {
        while(head<=tail&&i-q[head]+1>m)
            head++;
        while(head<=tail&&a[q[tail]]>=a[i])
            tail--;
        q[++tail]=i;
        if(i>=m)
            printf("%d ",a[q[head]]);    
    }
    puts("");
}

void Ask_MAX()
{
    head=1,tail=0;
    for(int i=1;i<=n;i++)
    {
        while(head<=tail&&i-q[head]+1>m)
            head++;
        while(head<=tail&&a[q[tail]]<=a[i])
            tail--;
        q[++tail]=i;
        if(i>=m)
            printf("%d ",a[q[head]]);
    }
    puts("");
}
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    Ask_MIN();
    Ask_MAX();
    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code

相关文章: