C++单调队列详解

  • Post author:
  • Post category:其他


单调队列到底是什么呢?

简单地按照字面意思来说,单调队列是一种队列(踢飞)

但是这种队列和普通的队列有着很大的区别,怎么说呢:



它的队首和普通的队列一样,只能删除元素。



而它的队尾既可以添加元素也可以删除元素。


通常来说也可以叫做输入受限的双端队列(栈)。



单调队列是做什么用的呢?

简单来说就是用来维护一段区间内的单调上升/下降性质,导出性质就是也可以用来维护一个区间内的最值。

我们先来看一道例题:

poj 2823

题目大意

给你n个数字和一个长度k,让你从前到后输出每个长度为k的区间内的最小值/最大值。

思路

1.暴力

从前到后每个区间遍历一遍,复杂度是O(n*k),铁定超时了

2.线段树维护区间最值

n的范围是10^6,线段树需要4*10^6的空间貌似不会爆,时间复杂度是O(nlogn),可以接受

但是对这道题而言,单调队列显然是优于线段树的另一种思路,先来了解一下它的

工作原理

假设现在我们维护一个递减(最大值)的单调队列,当一个数据想要入队时,需要执行以下操作:

①从队尾到队首开始遍历,如果碰到元素比待入队元素要小,那么这个元素便失去了作用(因为维护的是最大值,如果有一个比当前值小,那它一定不是最大的),将其出队。直到碰到一个比当前值大的元素,跳出循环;

②从队首开始清除

已经不符合条件的元素


(比如已经“过时”即不在当前区间内的元素,视题目而定);


在执行①之后,队列中所有之前元素都比当前元素大


执行②之后,队列中不再有“过时”元素,即此时队列中只有在区间内而且队首元素就是最大值。


复杂度

因为每个元素都只会入队一次和出队一次,所以复杂度为O(n)。

然后这道题目就可以成功地套上板子做出来了~~

不知道为什么c++提交就可以过,g++会超时,总之poj上还有好多奇怪的问题。。。。

#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
#include<map>
#include<string.h>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#define LL long long
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(a) (a)*(a)
#define lan(a,b) memset(a,b,sizeof(a))

using namespace std;

int a[1000010];
int num1[1000010];
int num2[1000010];
int b[1000010];

int c[1000010];
int d[1000010];

int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        lan(a,0);
        lan(b,0);
        lan(c,0);
        lan(d,0);
        lan(num1,0);
        lan(num2,0);
        int h1=0,t1=0;
        int h2=0,t2=0;
        for(int i=1;i<=n;i++)
        {
            int tem;
            scanf("%d",&tem);
            while(h1<t1&&a[t1-1]>tem) t1--;
            a[t1++]=tem;
            num1[t1-1]=i;
            while(i-num1[h1]+1>k) h1++;
            if(i>=k)
                c[i]=a[h1];
            while(h2<t2&&b[t2-1]<tem) t2--;
            b[t2++]=tem;
            num2[t2-1]=i;
            while(i-num2[h2]+1>k) h2++;
            if(i>=k)
                d[i]=b[h2];
        }
        for(int i=k;i<=n;i++)
        {
            if(i==n)
                printf("%d\n",c[i]);
            else
                printf("%d ",c[i]);
        }
         for(int i=k;i<=n;i++)
        {
            if(i==n)
                printf("%d\n",d[i]);
            else
                printf("%d ",d[i]);
        }
    }
    return 0;
}



版权声明:本文为Dale_zero原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。