浅析单调队列
9月 16 2014 更新日期:9月 16 2014
大家应该了解什么是队列,那么在队列前面加上“单调”。意思也是显而易见的。就是这个队列是从前往后单调递增或者单调递减的。
如:{a1,a2,a3,a4……an}满足a1<=a2<=a3……<=an,a序列便是单调递增序列。
同理递减队列也是存在的。
单调队列的出现能够简化问题,队首元素便是最大(小)值,这样。选取最大(小)值的复杂度便为o(1),因为队列的性质,每一个元素入队一次,出队一次,维护队列的复杂度均摊下来便是o(1)。
怎样维护单调队列呢,以单调递增序列为例:
-
假设队列的长度一定,先推断队首元素是否在规定范围内,假设超范围则增长队首。
-
每次增加元素时和队尾比較,假设当前元素小于队尾且队列非空,则减小尾指针。队尾元素依次出队,直到满足队列的调性为止。
比如:
队列是一个单调递增的队列:
1 ,5 , 7 , 9。 如今要插入一个 6。
由于 9 > 6 ,所以 9 出队列
---> 1 , 5, 7。
由于要 7 > 6 ,所以 7 出队列
---> 1, 5 .
6放在队列尾部,终于队列变为:
---> 1,5,6.
说完了单调队列的性质。那么我们该怎样运用呢?
简单的单调队列的应用:
1.果子合并问题
【问题描写叙述】
在一个果园里。多多已经将全部的果子打了下来,并且按果子的不同种类分成了不同的堆。多多决定把全部的果子合成一堆。
每一次合并,多多能够把两堆果子合并到一起。消耗的体力等于两堆果子的重量之和。
能够看出。全部的果子经过n-1次合并之后,就仅仅剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
由于还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每一个果子重量都为1,而且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
比如有3种果子,数目依次为1,2。9。能够先将1、2堆合并,新堆数目为3,耗费体力为3。接着。将新堆与原先的第三堆合并。又得到新的堆。数目为12。耗费体力为12。所以多多总共耗费体力=3+12=15。能够证明15为最小的体力耗费值。
【输入文件】
输入文件fruit.in包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包括n个整数。用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。
【输出文件】
输出文件fruit.out包括一行,这一行仅仅包括一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。
【例子输入】
3
1 2 9
【例子输出】
15
【数据规模】
对于30%的数据,保证有n<=1000:
对于50%的数据。保证有n<=5000;
对于所有的数据,保证有n<=10000。
这个题目非常的经典,发放也非常多。能够採用快排或者堆,其思想都是选取当前最小的两个堆进行合并。
复杂度均为O(nlogn),假设用有序队列维护,时间复杂度为O(n)。
每次选取进行合并的两堆,不是最先给定的堆,就是合并最初堆若干次后得到的新堆。所以须要维护两个单调递增队列。一个队列存最初给定的堆的值(1),一个存合并后得到的新值(2)。
每次选择时有三种状态:
-
选取队一的队首两个
-
选取队2的的队首两个
-
选取二者队首各一个
仅仅需对每一个队列的指针做对应的更改。
特别注意初始化。
这道题非常好的运用了题目中决策的单调性,对初始对经行排序,保证了其单调性。而对于新产生的堆来说。一旦有新元素增加当中。则新元素一定大于原有元素。(非常显然,因为队列1的单调性)。
也就是说,队列的单调性是自然而然的。
是不须要维护的。
要善于观察分析,才干发现。
Window
poj2823
Description
An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example:
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window position | Minimum value | Maximum value |
---|---|---|
[1 3 -1] -3 5 3 6 7 | -1 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5] 3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 3 | 7 |
Your task is to determine the maximum and minimum values in the sliding window at each position.
Input
The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.
Output
There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.
Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7
题目大意:给出一组数。一个固定大小的窗口在这个数组上滑动。要求出每次滑动该窗口内的最大值和最小值。
这就是典型的单调队列,单调队列的作用就在此。单调队列的队首为区间内的最值,但是整个队列不用保持单调。
用两个队列分别处理最大值和最小值。在此说明一下最大值;
往队列中增加值num时。从队尾开始扫,直到遇到一个小于num的d值,将num插入d的后一位。
之后的元素全部无效化(不管后面的元素就可以)。查找最大值的时候。从队首开始找。如果该元素没在此时的区间的话。查找下一个。直到找到满足条件的第一个元素。这个元素便是最值。
求最小值和最大值大同小异,只需要将增加值num的条件改一下就能够。
广告印刷
【问题描写叙述】
近期。afy决定给TOJ印刷广告,广告牌是刷在城市的建筑物上的。城市里有紧靠着的N个建筑。
afy决定在上面找一块尽可能大的矩形放置广告牌。我们如果每一个建筑物都有一个高度,从左到右给出每一个建筑物的高度H1,H2…HN。且0<Hi<=1,000,000,000,而且我们如果每一个建筑物的宽度均为1。要求输出广告牌的最大面积。
【输入文件】
第一行是一个数n (n<= 400,000 )
第二行是n个数。分别表示每一个建筑物高度H1,H2…HN,且0<Hi<=1,000,000,000。
【输出文件】
输出文件 ad.out 中一共同拥有一行,表示广告牌的最大面积。
【输入例子】
6
5 8 4 4 8 4
【输出例子】
24
【分析】
终于的广告牌一定等于某个建筑物的高度×其能达到的最大长度
如今,建筑物的高度已知,如今仅仅须要知道每一个高度能达到的最大长度是多少。因为n是400000。我们仅仅能用O(n)或O(nlogn)的算法。能够使用rmq。在后边的论文中会讲到。
如今讲时间复杂度为o(n)的单调队列的方法。
继续上边的思路,对于每一个建筑物,仅仅须要找到其可以扩展到的最大宽度就可以。
也就是这个建筑物的左右两边的比它低或等于它的建筑物个数。
怎样用单调队列呢?
我们从1~n一次进队。维护一个单调递减序列。每次增加元素后维护其单调性,当然这样做必定会使一些元素出队。出队的元素一定要比当前增加的元素小,也就是说当前元素就是出队的元素能在右側达到的最远的建筑物!
注意,要让h[n+1]=0而且让该元素入队一次(会使当前队列中的全部元素出队),保证每一个元素都有其“右极限”的值。
要求“左极限”同理。仅仅需从n~0循环就可以,注意0
这道题是对单调队列的变形使用。因为问题的结果具有单调性。非常好的利用出队元素的特性.
单调队列在动态规划中的应用
做动态规划时经常会见到形如这种转移方程:
f[x] = max or min{g(k) | b[x] <= k < x} + w[x]
(当中b[x]随x单调不降,即b1<=b2<=b[3]<=…<=b[n])
(g[k]表示一个和k或f[k]有关的函数,w[x]表示一个和x有关的函数)
这个方程如何求解呢?我们注意到这样一个性质:假设存在两个数j, k,使得j <= k。并且g(k) <= g(j)。则决策j是毫无用处的。由于依据b[x]单调的特性,假设j能够作为合法决策,那么k一定能够作为合法决策,又由于k比j要优,(注意:在这个经典模型中,“优”是绝对的,是与当前正在计算的状态无关的),所以说。假设把待决策表中的决策依照k排序的话,则g(k)必定是不降的。
这样。就引导我们使用一个单调队列来维护决策表。
对于每个状态f(x)来说,计算过程分为下面几步:
-
队首元素出队,直到队首元素在给定的范围中。
-
此时,队首元素就是状态f(x)的最优决策。
-
计算g(x)。并将其插入到单调队列的尾部,同一时候维持队列的单调性(不断地出队,直到队列单调为止)。
反复上述步骤直到全部的函数值均被计算出来。不难看出这种算法均摊时间复杂度是O(1)的。因此求解f(x)的时间复杂度从O(n^2)降到了O(n)。
单调队列指一个队列中的全部的数符合单调性(单调增或单调减),在信息学竞赛的一些题目上应用,会降低时间复杂度
单调队列的每一个元素通常会存储两个值:
1.在原数列中的位置(下标)
2.该元素在动态规划中的状态值(价值)
单调队列同一时候保证这两个值单调。
烽火传递
描写叙述 Description
烽火台又称烽燧。是重要的防御设施,一般建在险要处或交通要道上。一旦有敌情发生。白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情。
在某两座城市之间有n个烽火台,每一个烽火台发出信号都有一定的代价。为了使情报准确的传递。在m个烽火台中至少要有一个发出信号。现输入n、m和每一个烽火台发出的信号的代价,请计算总共最少须要话费多少代价,才干使敌军来袭之时。情报能在这两座城市之间准确的传递!。。
输入格式 Input Format
第一行有两个数n,m分别表示n个烽火台,在m个烽火台中至少要有一个发出信号。
第二行为n个数。表示每个烽火台的代价。
输出格式 Output Format
一个数。即最小代价。
例子
输入:
5 3
1 2 5 6 2
输出:
4
时间限制 Time Limitation
各个測试点1s
凝视 Hint
1<=n,m<=1,000,000
分析
要用动态规划的方法解决。我们能够写出这种方程f[i]:=min{f[j]}+a[i](i-m<=j<i-1)(由于要保证i之前的3个中必须存在被点亮的烽火台)。单纯这样循环会造成超时。
我们想到了用单调队列进行优化,因为随着i的循环,每次仅仅有一个i进入决策区间也仅仅有一个i出决策区间,因为每次选取决策区间中的最小值,所以维护一个单调递增序列。每次取出队首元素就可以。
为什么能够将队尾元素无情的删去呢?因为后进队的序列同一时候满足在原序列中的位置更靠后和其在动态规划中的价值更大。
这样选取这个元素就要比选取之前的不论什么一个决策要优。所以之前被删掉的决策都是没用的。
这道题的本质就是用单调队列维护了决策本身的价值和其在原序列中位置的同一时候单调。
要特别注意单调队列中的值是决策在原决策序列中的位置。
參考资料:
-
单调队列及其应用 [推荐]
版权声明:本文博主原创文章。博客,未经同意不得转载。