浅析单调队列

浅析单调队列

 9月 16 2014 更新日期:9月 16 2014

文章目录
  1. 1. 简单的单调队列的应用:
    1. 1.1. 1.果子合并问题
    2. 1.2. Window
      1. 1.2.1. poj2823
    3. 1.3. 广告印刷
  2. 2. 单调队列在动态规划中的应用
  3. 3. 参考资料:

大家应该了解什么是队列,那么在队列前面加上“单调”,意思也是显而易见的,就是这个队列是从前往后单调递增或者单调递减的。

如:{a1,a2,a3,a4……an}满足a1<=a2<=a3……<=an,a序列便是单调递增序列。同理递减队列也是存在的。

单调队列的出现可以简化问题,队首元素便是最大(小)值,这样,选取最大(小)值的复杂度便为o(1),由于队列的性质,每个元素入队一次,出队一次,维护队列的复杂度均摊下来便是o(1)。

如何维护单调队列呢,以单调递增序列为例:

  1. 如果队列的长度一定,先判断队首元素是否在规定范围内,如果超范围则增长队首。

  2. 每次加入元素时和队尾比较,如果当前元素小于队尾且队列非空,则减小尾指针,队尾元素依次出队,直到满足队列的调性为止。

例如:

队列是一个单调递增的队列:
1 ,5 , 7 , 9。    现在要插入一个 6;
因为 9 > 6 ,所以  9  出队列
---> 157
因为要 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)。

每次选择时有三种状态:

  1. 选取队一的队首两个

  2. 选取队2的的队首两个

  3. 选取二者队首各一个

只需对每个队列的指针做相应的更改。

特别注意初始化。

这道题很好的运用了题目中决策的单调性,对初始对经行排序,保证了其单调性。而对于新产生的堆来说,一旦有新元素加入其中,则新元素一定大于原有元素。(很显然,由于队列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

【输入文件】

第一行是一个数n (n<= 400,000 )

第二行是n个数,分别表示每个建筑物高度H1,H2…HN,且0

【输出文件】

输出文件 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)来说,计算过程分为以下几步:

  1. 队首元素出队,直到队首元素在给定的范围中。

  2. 此时,队首元素就是状态f(x)的最优决策,

  3. 计算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的循环,每次只有一个i进入决策区间也只有一个i出决策区间,由于每次选取决策区间中的最小值,所以维护一个单调递增序列,每次取出队首元素即可。

为什么可以将队尾元素无情的删去呢?由于后进队的序列同时满足在原序列中的位置更靠后和其在动态规划中的价值更大。这样选取这个元素就要比选取之前的任何一个决策要优,所以之前被删掉的决策都是无用的。

这道题的本质就是用单调队列维护了决策本身的价值和其在原序列中位置的同时单调。

要特别注意单调队列中的值是决策在原决策序列中的位置。


参考资料:

  • 单调队列及其应用 [推荐]

  • 合并果子【单调队列】

你可能感兴趣的:(算法)