单调队列及其应用

上传人:仙*** 文档编号:202573502 上传时间:2023-04-22 格式:DOC 页数:16 大小:93.50KB
返回 下载 相关 举报
单调队列及其应用_第1页
第1页 / 共16页
单调队列及其应用_第2页
第2页 / 共16页
单调队列及其应用_第3页
第3页 / 共16页
点击查看更多>>
资源描述
COCI 2009 Ograda_题目单调队列及其应用单调队列,望文生义,就是指队列中的元素是单调的。如:a1,a2,a3,a4an满足a1=a2=a3=an,a序列便是单调递增序列。同理递减队列也是存在的。单调队列的出现可以简化问题,队首元素便是最大(小)值,这样,选取最大(小)值的复杂度便为O(1),由于队列的性质,每个元素入队一次,出队一次,维护队列的复杂度均摊下来便是O(1)。如何维护单调队列呢,以单调递增序列为例:1、如果队列的长度一定,先判断队首元素是否在规定范围内,如果超范围则增长队首。2、每次加入元素时和队尾比较,如果当前元素小于队尾且队列非空,则减小尾指针,队尾元素依次出队,直到满足队列的调性为止要特别注意头指针和尾指针的应用。下面介绍单调队列的具体应用:一、单调队列的直接应用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。【样例输入】31 2 9【样例输出】15【数据规模】对于30%的数据,保证有n = 1000;对于50%的数据,保证有n = 5000;对于全部的数据,保证有n = 10000。分析:这个题目非常的经典,发放也很多,可以采用快排或者堆,其思想都是选取当前最小的两个堆进行合并。复杂度均为O(nlOgn),如果用有序队列维护,时间复杂度为O(n)。每次选取进行合并的两堆,不是最先给定的堆,就是合并最初堆若干次后得到的新堆,所以需要维护两个单调递增队列,一个队列存最初给定的堆的值(1),一个存合并后得到的新值(2)。每次选择时有三种状态:1.选取队一的队首两个 2.选取队2的的队首两个3.选取二者队首各一个只需对每个队列的指针做相应的更改。特别注意初始化。这道题很好的运用了题目中决策的单调性,对初始对经行排序,保证了其单调性。而对于新产生的堆来说,一旦有新元素加入其中,则新元素一定大于原有元素。(很显然,由于队列1的单调性)。也就是说,队列的单调性是自然而然的。是不需要维护的。要善于观察分析,才能发现。【程序代码】prOgram because_Of_lOve;var a,b:array1.100000 Of lOngint; h1,h2,t2,temp,n,i,ans:lOngint;functiOn min(a,b,c:lOngint):lOngint;begin if ab then min:=a else min:=b; if cmin then min:=c;end;prOcedure sOrt(l,r:lOngint);var i,j,mid,temp:lOngint;begin i:=l; j:=r; mid:=al+randOm(r-l);/随机化排序 repeat while aimid dO dec(j); if ij; if ir then sOrt(i,r); if l2;/保证程序在需要的地方停止,由于要选极小值 sOrt(1,n); an+1:=maxlOngint2;/作用同上 an+2:=maxlOngint2; h1:=1; h2:=1; t2:=0; fOr i:=1 tO n-1 dO begin temp:=min(ah1+ah1+1,ah1+bh2,bh2+bh2+1); if temp=ah1+ah1+1 then inc(h1,2) else if temp=ah1+bh2 then begin inc(h1);inc(h2);end else inc(h2,2); inc(t2); bt2:=temp; inc(ans,temp); end; writeln(ans);end. 2WindOw给定你n个数aian一个确定长度的区间,让你求出每个区间内的最大值,并按照顺序输出输入 N,k A1an输出 每个区间内的最大值分析 由于该题的区间长度一定,我们可以用一个长度为k(A,B)的队列来维护数据的有序性。 剩下的问题就是如何维护队列的有序性了。 最前面已经说到了,代码也不再赘余。 3、志愿者选拔(fOj 1894)世博会马上就要开幕了,福州大学组织了一次志愿者选拔活动。参加志愿者选拔的同学们排队接受面试官们的面试。参加面试的同学们按照先来先面试并且先结束的原则接受面试官们的考查。面试中每个人的人品是主要考查对象之一。(提高人品的方法有扶老奶奶过街,不闯红灯等)作为主面试官的JOhn想知道当前正在接受面试的同学队伍中人品值最高的是多少。于是他请你帮忙编写一个程序来计算。 Input输入数据第一行为一整数T,表示有T组输入数据。每组数据第一行为”START”,表示面试开始接下来的数据中有三种情况: 1 C NAME RP_VALUE 名字为NAME的人品值为RP_VALUE的同学加入面试队伍。(名字长度不大于5,0 = RP_VALUE =last)如果是C命令,则将该元素加入队列中,并和队尾元素比较,维护队列的单调性。这里考虑一个问题,当前元素加如后对队尾元素为什么可以毫无保留的删去呢?因为当前加入的元素比队尾元素大,且该元素比队尾元素入队晚(也就是该元素比队尾元素晚出队),所以只要该元素在队列中,就一定不会选取队尾元素。也就是当前状态一定比队尾元素的状态更优。这里一定要理解深刻,这是队列的本质。因此,这题的单调队列中维护的一个属性是元素的价值,一个属性是单调队列中的元素在原序列中的位置。注意,q中的值是该元素在原序列中的位置! 【程序代码】prOgram because_Of_lOve;var a,q:array0.1000000 Of lOngint; zu,i,l,r,last,tt,w:lOngint; s:string;begin readln(zu); fOr i:=1 tO zu dO begin tt:=0; fillchar(a,sizeOf(a),0); readln(s); l:=1; r:=0; last:=0; while sEND dO begin readln(s); case s1 Of G:begin inc(last); while (ql=last)and(lr then writeln(-1) else writeln(aql); end; C:begin inc(tt); delete(s,1,2); w:=pOs( ,s); delete(s,1,w); val(s,att); while(attaqr)and(l=r)dO dec(r); inc(r); qr:=tt; end; end; end; end;end. 4、广告印刷【问题描述】最近,afy决定给TOJ印刷广告,广告牌是刷在城市的建筑物上的,城市里有紧靠着的N个建筑。afy决定在上面找一块尽可能大的矩形放置广告牌。我们假设每个建筑物都有一个高度,从左到右给出每个建筑物的高度H1,H2HN,且0Hi=1,000,000,000,并且我们假设每个建筑物的宽度均为1。要求输出广告牌的最大面积。【输入文件】中的第一行是一个数n (n= 400,000 )第二行是n个数,分别表示每个建筑物高度H1,H2HN,且0Hi=1,000,000,000。【输出文件】输出文件 ad.Out 中一共有一行,表示广告牌的最大面积。【输入样例】65 8 4 4 8 4【输出样例】24 【分析】最终的广告牌一定等于某个建筑物的高度其能达到的最大长度现在,建筑物的高度已知,现在只需要知道每个高度能达到的最大长度是多少。由于n是400000,我们只能用O(n)或O(nlOgn)的算法。可以使用rmq,在后边的论文中会讲到。现在讲时间复杂度为O(n)的单调队列的方法。继续上边的思路,对于每个建筑物,只需要找到其能够扩展到的最大宽度即可。也就是这个建筑物的左右两边的比它低或等于它的建筑物个数。如何用单调队列呢?我们从1n一次进队,维护一个单调递减序列。每次加入元素后维护其单调性,当然这样做必然会使一些元素出队,出队的元素一定要比当前加入的元素小,也就是说当前元素就是出队的元素能在右侧达到的最远的建筑物!注意,要让hn+1=0并且让该元素入队一次(会使当前队列中的所有元素出队),保证每个元素都有其“右极限”的值。要求“左极限”同理,只需从n0循环即可,注意0这道题是对单调队列的变形使用。由于问题的结果具有单调性,很好的利用出队元素的特性。 【程序代码】prOgram because_Of_lOve;var q:array0.40000 Of lOngint; n,i,l,r,ans:lOngint; h,left,right:array0.40000 Of lOngint; begin readln(n); fOr i:=1 tO n dO read(hi); l:=1; r:=0; fOr i:=1 tO n+1 dO begin while (hihqr)and(l=r) dO begin rightqr:=i; dec(r); end; inc(r); qr:=i; end; l:=1; r:=0; fOr i:=n dOwntO 0 dO begin while (hihqr)and(lans then ans:=(righti-lefti-1)*hi; writeln(ans);end. 5、总结单调队列的应用仍有很多实例,这一不能一一道出。 首先考虑问题需要的时间复杂度如果是O(n)的算法,单调队列是首选。 其次要善于观察分析,发现题目中的单调性。决策的单调(合并果子),要求问题的特性(windOw),元素价值和其在原序列中位置的单调(志愿者),问题结果的单调(广告印刷)。 二、单调队列在优化动态规划中的应用 做动态规划时常常会见到形如这样的转移方程:fx = max Or ming(k) | bx = k x + wx(其中bx随x单调不降,即b1=b2=b3=.=bn)(gk表示一个和k或fk有关的函数,wx表示一个和x有关的函数)这个方程怎样求解呢?我们注意到这样一个性质:如果存在两个数j, k,使得j = k,而且g(k) = g(j),则决策j是毫无用处的。因为根据bx单调的特性,如果j可以作为合法决策,那么k一定可以作为合法决策,又因为k比j要优,(注意:在这个经典模型中,“优”是绝对的,是与当前正在计算的状态无关的),所以说,如果把待决策表中的决策按照k排序的话,则g(k)必然是不降的。这样,就引导我们使用一个单调队列来维护决策表。对于每一个状态f(x)来说,计算过程分为以下几步:1、 队首元素出队,直到队首元素在给定的范围中。2、 此时,队首元素就是状态f(x)的最优决策,3、计算g(x),并将其插入到单调队列的尾部,同时维持队列的单调性(不断地出队,直到队列单调为止)。重复上述步骤直到所有的函数值均被计算出来。不难看出这样的算法均摊时间复杂度是O(1)的。因此求解f(x)的时间复杂度从O(n2)降到了O(n)。单调队列指一个队列中的所有的数符合单调性(单调增或单调减),在信息学竞赛的一些题目上应用,会减少时间复杂度单调队列的每个元素一般会存储两个值:1.在原数列中的位置(下标)2.该元素在动态规划中的状态值(价值)单调队列同时保证这两个值单调。 下面看几个优化的实例:1、 烽火传递描述 DescriptiOn (烽火传递)烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情。在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价。为了使情报准确的传递,在m个烽火台中至少要有一个发出信号。现输入n,m和每个烽火台发出的信号的代价,请计算总共最少需要话费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递。例如,有5个烽火台,它们发出信号的代价为1,2,5,6,2,且m为3,则总共最少花费的代价为4,即由第2个和第5个烽火台发出信号。输入格式 Input FOrmat 第一行有两个数n,m分别表示n个烽火台,在m个烽火台中至少要有一个发出信号。 第二行为n个数,表示每一个烽火台的代价。输出格式 Output FOrmat 一个数,即最小代价。 样例5 31 2 5 6 2 4时间限制 Time LimitatiOn 各个测试点1s注释 Hint 1=n,m=1,000,000分析要用动态规划的方法解决。我们可以写出这样的方程fi:=minfj+ai(i-m=ji-1)(因为要保证i之前的3个中必须存在被点亮的烽火台)。单纯这样循环会造成超时。我们想到了用单调队列进行优化,由于随着i的循环,每次只有一个i进入决策区间也只有一个i出决策区间,由于每次选取决策区间中的最小值,所以维护一个单调递增序列,每次取出队首元素即可。为什么可以将队尾元素无情的删去呢?由于后进队的序列同时满足在原序列中的位置更靠后和其在动态规划中的价值更大。这样选取这个元素就要比选取之前的任何一个决策要优,所以之前被删掉的决策都是无用的。这道题的本质就是用单调队列维护了决策本身的价值和其在原序列中位置的同时单调。要特别注意单调队列中的值是决策在原决策序列中的位置。【程序代码】prOgram because_Of_lOve;var a,f,q:array0.1000000 Of lOngint; m,n,l,r,i,ans:lOngint;begin readln(n,m); fOr i:=1 tO n dO read(ai); f0:=0; l:=1;r:=0; fOr i:=1 tO n dO begin while(qli-m)and(l=r)dO inc(l); while(fi-1fqr)and(lfi then ans:=fi; writeln(ans);end. 2、难题的解决给定一个序列,读入n,k和a1an,让你求出该序列中包含第k项的最长上升子序列长度。 1=n=300000 k=n分析不要被问题的表面现象迷惑,既然要包含第k项,那么在k项的左边,比第k项大的元素一定不会进入目标序列。同理在k项的右边,比第k项小的元素一定也不会进入目标队列。那么我们直接把这些无用的元素删去。剩下的队列就好看得多。在k项之前的元素都比k小,之后的都比k项大。我们只需从1k-1求一遍最长上升子序列,再从k+1n求一遍最长上升子序列即可。最后结果为二者的值加1。由于数据范围很大,用传统的n2算法无法完成任务。所以本题地关键是(nlOgn)的lis假如xyt且axay,fx=fy,也就是说选取x,y都可以更新得到相同的的ft的值,那么选取谁呢。显然选取x会更优,因为在axay中如果存在az,使得axazay那么就可以通过x得到更长的最长不降子序列的值。根据f的值进行分类,我们只需要保留满足ft=k的所有at中的最小值,设dk记录这个值,即dk:=minat(ft=k)我们注意到d有两个特点:1. dk的值在整个过程中是单调不上升的。2. d数组是有序的。即d1d2.dlen将其直接假如d,inc(len)得到更长的序列否则,在d中查找到第一个比该元素小的元素dk,将该元素加入,dk+1:=x;如果使用顺序查找效率会很低,所以采用二分查找,将其复杂度降为lOg级,难点在于二分查找。 本题中的单调队列的出现时利用决策的性质,用元素在动归中的价值分类。在入队操做时并未让所有元素出队,而是直接插入相应位置,这是根据题目的特殊性决定的。对于一个单调的序列往往用二分法。当然这种发法也可推广到其他的最XX序列问题上面说的已经很详细,代码就省了。 三、一些总结单调队列有很多的应用,这里只是介绍了一部分,特别是在优化动态规划时。希望读者能在实践中形成自己的方法 - 16 2011-08-09
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!