单调队列

最后都变了- 提交于 2019-11-27 16:49:22

单调队列,顾名思义,就是一个元素具有单调性的队列,{1,2,5}这是一个单调增的队列,{6,4,1}这是一个单调减的队列。

其用途我们通过一道例题来说明

洛谷P1866 滑动窗口

现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

例如

题意是有一个长度为N的序列,还有一个长度为k的区间,区间每次向右滑动一位,求每次滑动过后的最大值和最小值。

这道题可以用线段树或ST表来解决,这里仅仅说单调队列。

如果我们维护一个队列,保持队首是要查找的值,那么每次滑动的时候,我们输出队首即可。

现在问题是,我们怎么样维护这样一个合法的队首,我们就应用了单调队列。

这里以最大值为例,试想,如果碰到一个新的元素num[j](i<j),如果num[i]>num[j],那么num[i]和num[j]都是有用的,

因为如果窗口往后滑,num[i]滑出了区间,弹出队列,那么num[j]可能会成为那个合法的队首,所以num[i]和num[j],

我们都要记下来,再如果num[i]<=num[j](i<j),那么num[i]永远也不会成为将要查询区间的合法元素,所以直接弹掉

即可,明确了这一点剩下只是一些简单的操作。

在队列中记下每个元素在原序列中的位置,如果这个元素不在查询序列中了就弹掉。

如果碰到num[i]<=num[j](i<j),就弹掉队尾破坏单调性的元素,知道满足单调性了,就将新的元素加入。

如果碰到num[i]>num[j] (i<j),就把num[j]加入队尾,因为没有破坏单调性。

最小值的查询相反即可。

P1866 代码(维护一个单调增队列查询最小值,再维护一个单调减队列查询最大值)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 1000005
 6 
 7 using namespace std;
 8 
 9 struct node
10 {
11     int data,pos;
12 };
13 node q[maxn];
14 int num[maxn],n,k;
15 
16 int main()
17 {
18     scanf("%d%d",&n,&k);
19     for(int i=1;i<=n;i++)
20         scanf("%d",&num[i]);
21     int head=1,tail=0;
22     for(int i=1;i<=n;i++)
23     {
24         while(head<=tail&&q[head].pos+k<=i) head++;
25         while(head<=tail&&q[tail].data>=num[i]) tail--;
26         q[++tail].data=num[i]; q[tail].pos=i;
27         if(i>=k) printf("%d ",q[head].data);
28     }
29     printf("\n");
30     head=1; tail=0;
31     for(int i=1;i<=n;i++)
32     {
33         while(head<=tail&&q[head].pos+k<=i) head++;
34         while(head<=tail&&q[tail].data<=num[i]) tail--;
35         q[++tail].data=num[i]; q[tail].pos=i;
36         if(i>=k) printf("%d ",q[head].data); 
37     }
38     return 0;
39 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!