单调队列

单调队列

性质:

    在一个队列中,所有的数都是保持上升或者下降序列。

作用:

    可以快速求出某一段的最大最小值,类似于线段树,但是比线段树代码量短,速度比线段树快,主要用于优化,最常见的为优化DP,容易理解。

 

维护方法:

    怎样使队列保持单调呢?这就要明白怎样去维护这个队列,即怎样插入一个元素到队列,又怎样删除队首的元素使元素的个数不超过区间的限制。

 

1)初始化的时候,我们把队首赋成第一个元素。

2)依次枚举余下的N-1个元素,把当前未入队的元素与队尾元素进行比较,如果队尾元素的值小于当前未入队的元素,就将队尾元素删除,(因为队尾元素不可能成为最大值了)如果队列非空,继续与队尾元素比较,直到队尾比当前这个未入队的元素大,或者队列为空,然后将当前这个未入队的元素入队。

3)由于单调队列有指定区间大小的限制,入队之后,需要将队尾元素与队首元素的下标进行计算,看是否超过区间范围,若超过了就把队首元素出队。

4)n个元素经过多次进队出队操作,最后得到的队列的队首元素就是最大值了,直接输出即可。

时间复杂度:

    由于每个元素最多进队一次出队一次,复杂度为O(n),有效地减少了时间复杂度。在信息学竞赛中应用较多。

问题引入:

    有一个数列为:8, 7, 12, 5, 4, 2,  16,  9,  17,8,每次从左到右选取3个数,输出每个区间中的最大值。

    我们可以把这个问题假想成有一个窗口从这一列数的左边移动到右边,透过这个窗口我们最多可以看到3个数,输出每次从窗口中看到的数的最大值。

    很直观的一种解法,那就是从数列的开头,将窗放上去,8, 7, 12, 5, 4, 2,  16,  9,17,8,然后找到这3个数的最大值12;然后窗后移一个单元,8, 7, 12, 5, 4, 2,  16,9,17,8;继续找到3个数中的最大值12......依次找到每个区间的最大值。

具体实现:

     对于上面问题中的数列:8, 7, 12, 5, 4, 2,  16,  9,  17,8,可以构造一个长度为3的单调递减队列。

     i=1:插入8,队列为:(8)      {i=1<3,不输出队首8}

     i=2:插入7,队列为:(8,7)    {i=2<3,不输出队首8}

     i=3:插入12,队列为:(12,8,7){12大于7和8,依次删除队尾的7和8,并插入12到队列,且i=3了,故输出队首的12}

     i=4:插入5,队列为:(12,5)    {i≥3,输出队首12}

     i=5:插入4,队列为:(12,5,4) {i≥3,输出队首12}

     i=6:插入2,队列为:(12,5,4,2){i≥3,2插入后,因为队列中的元素多于了3个,队首的12没用了故被删除,输出此时的队首5,}

     i=7:插入16,队列为:(16  5,4,2) {16大于队尾的2、4、5,依次删除2、4、5,并输出队首16}

     i=8:插入9,队列为:(16,9)        {i≥3,输出队首16}

     i=9:插入17,队列为:(17 16,9)    {i≥3,输出队首17}

     i=10:插入8,队列为(17,8)       {i≥3,输出队首17}

    (以上部分内容借鉴了湖南省醴陵市第一中学曾妞妞的论文)

裸题:

       Poj2823

       或 洛谷 滑动窗口

 

代码实现

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1000005;
int q[maxn],a[maxn],e,n;
int main()
{
	freopen("2117.in","r",stdin);
	freopen("2117.out","w",stdout);
	ios::sync_with_stdio(false);
	cin>>n>>e;
	for(int i=1;i<=n;i++) 
	{
		cin>>a[i];
	}
	int head=1,tail=0;
	for(int i=1;i<=n;i++)
	{
		while(head<=tail&&a[q[tail]]>=a[i]&&tail!=0) tail--;
		q[++tail]=i;
		while(i-q[head]+1>e) head++;
		if(i>=e) printf("%d ",a[q[head]]);
	}//取小值
	cout<<endl;
	head=1,tail=0;
	for(int i=1;i<=n;i++)
	{
		while(head<=tail&&a[q[tail]]<=a[i]) tail--;
		q[++tail]=i;
		while(i-q[head]+1>e) head++;
		if(i>=e) printf("%d ",a[q[head]]);
	}//取大值
	return 0;
}

  

posted @ 2017-08-19 14:08  yiyiyizqy  阅读(135)  评论(0编辑  收藏  举报