先说点题外话,为什么要讲单调队列呢?因为我恨透了它的题解。前段时间,我一听到“单调队列”,我就心想:完了,是单调队列。因为我认为很难,好好看看,原来是题解讲得太复杂了,这十分简单,易理解,易实现。
何为单调队列,就是一个由大到小或由小到大排列的队列,如图:
到这里,大家一定都理解,但,这有什么用呢。
这是用来解决一串数中一个移动的区间求最大、最小值的算法。(这里声明与RMQ的区别:RMQ——不改变数值,多次求任意一段区间最大最小值,单调队列——不改变数值,但区间长度确定)
如图:

————————————————————————————————————
那么我们这就以求最大值为例:
设队列
n
u
m
num
num(存储位置),枚举i。
这个队列这样做:
把队列中位置已超范围(不在框中)的数从头到尾删掉
把
a
[
i
]
a[i]
a[i]从队尾往前删(循环枚举i),只要队尾的值小于
a
[
i
]
a[i]
a[i]就退队(
w
h
i
l
e
while
while语句),直到不小于。
在队尾插入
a
[
i
]
a[i]
a[i]
O
K
!
OK!
OK!
证明一些东西:
一、为什么从头到尾删不会少删?
因为加入队列只能从队尾加入,队头的数一定是先加入的,至于队尾后先加入的数已经被删掉,不会再用到(再说都删了)。
二、为什么一个数
a
a
a在另一个数
b
b
b前,且数
a
a
a小于数
b
b
b,
a
a
a就可以退队了?
因为数一定先失效(出框),如果
a
a
a在,
b
b
b也在(指b入队时),
b
b
b大于
a
a
a,
a
a
a就一定不是最大的,废了。
————————————————————————————————————
代码:
for (i=1;i<=n;i++)//枚举a[i]
{
while (num[st]<i-m&&st<en) st++;//删除队头
while (a[num[en]]>a[i]&&st<en) en--;//删除队尾
num[++en]=i;//加入a[i]
}
如还有问题请留言。