RMQ
更新时间:2024-01-13 11:35:01 阅读量: 教育文库 文档下载
RMQ
RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。 简介
主要方法及复杂度如下:
1、朴素(即搜索),O(n)-O(qn) online。 2、线段树,O(n)-O(qlogn) online。
3、ST(实质是动态规划),O(nlogn)-O(q) online。
ST算法(Sparse Table),以求最大值为例,设d[i,j]表示[i,i+2^j-1]这个区间内的最大值,那么在询问到[a,b]区间的最大值时答案就是max(d[a,k], d[b-2^k+1,k]),其中k是满足2^k<=b-a+1(即长度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
d的求法可以用动态规划,d[i,j]=max(d[i,j-1],d[i+2^(j-1),j-1])。 4、RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(q) online。
首先根据原数列,建立笛卡尔树,从而将问题在线性时间内规约为LCA问题。LCA问题可以在线性时间内规约为约束RMQ,也就是数列中任意两个相邻的数的差都是+1或-1的RMQ问题。约束RMQ有O(n)-O(1)的在线解法,故整个算法的时间复杂度为O(n)-O(1)。 ST算法
来看一下ST算法是怎么实现的(以最大值为例):
首先是预处理,用一个DP解决。设a是要求区间最值的数列,f[i,j]表示从第i个数起连续2^j个数中的最大值。例如数列3 2 4 5 6 8 1 2 9 7 ,f[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4??从这里可以看出f[i,0]其实就等于a[i]。这样,DP的状态、初值都已经有了,剩下的就是状态转移方程。我们把f[i,j](j≥1)平均分成两段(因为j≥1时,f[i,j]一定是偶数个数字),从i到i+2^(j-1)-1为一段,i+2^(j-1)到i+2^j-1为一段(长度都为2^(j-1))。用上例说明,当i=1,j=3时就是3,2,4,5 和6,8,1,2这两段。f就是这两段的最大值中的最大值。于是得到动规方程F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1])。
接下来是得出最值,也许你想不到计算出f有什么用处,一般要想计算max还是要O(logn),甚至O(n)。但有一个很好的办法,做到了O(1)。还是分开来,如在上例中我们要求区间[2,8]的最大值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由f[2,2]和f[5,2]得到。扩展到一般情
况,就是把区间[l,r]分成两个长度为2^n的区间(保证有f对应)。直接给出表达式:k:=trunc(ln(r-l+1)/ln(2));
ans:=max(F[l,k],F[r-2^k+1,k]); 这样就计算了从l开始,长度为2^k的区间和从r-2^k+1开始长度为2^k的区间的最大值(表达式比较繁琐,细节问题如加1减1需要仔细考虑),二者中的较大者就是整个区间[l,r]上的最大值。 标准算法
建立笛卡尔树
数组A[0,N-1]的笛卡尔树C是这样一棵二叉树:当N=0,它是一棵空树,否则它的根节点是A中的一个最小元素A[i](并以这个最小元素的下标i标记),而左右子树分别是A[0,i-1]和A[i+1,N-1]的一棵笛卡尔树。注意如果A中有相等的元素,则A的笛卡尔树不一定唯一,但在这里我们限定所用的最小元素为在数组中最先出现者,在此限制下笛卡尔树是唯一的。
容易看出,数组A在闭区间[l,r]上的最小值等同于笛卡尔树C中下标为l和r的两个顶点的最近公共祖先(LCA)的值。由此,RMQ问题可以转化为LCA问题。下面说明如何在O(n)时间内实现这一转化。
我们将要将A的元素依次插入笛卡尔树C。每次插入都可能使树的形态发生变化。为了在O(N)的时间内完成整个插入过程,考虑C的右链,即根结点、根结点的右儿子、根结点的右儿子的右儿子??组成的链。注意这些元素的下标和值都是递增的。下标最大,即将要插入的元素A[i]一定是新树右链的最后一个元素。原来的右链中,值比A[i]大的元素在新树中不再属于右链,这些元素组成的链成为A[i]的左子树的右链;原来右链中的其它元素加上A[i]组成了新的右链。初看起来,寻找分界点的最佳方法是O(logN)时间的二分查找;但是对于整个过程来说,O(NlogN)的时间复杂度不是最优的。关键在于一旦一个元素比A[i]大,它就从右链中被永久地移除了。如果按照从后到前的顺序判断一个元素是否大于A[i],则每次插入的时间复杂度为O(k+1),k为本次插入中移除的右链元素个数。因为每个元素最多进出右链各一次,所以整个过程的时间复杂度为O(N)。
用一个栈结构维护右链元素的下标,上述过程可以很容易地实现。(见下面代码部分) 转化为约束RMQ
为了将LCA问题转化为约束RMQ,我们注意到任意树中两个结点u和v的LCA就是在一次从树根开始的深度优先搜索中,在u和v之间(包括回溯时)到达的结点中层数最小的一个。为了利用这一事实,我们建立三个数组:
E[1,2*N-1]:在一次深度优先搜索(恰好是树的一次欧拉环游)中每一步到达的结点。
L[1,2*N-1]:E中对应结点在树中的层数。
H[1,N]:每个结点在E中某一次出现的下标(不妨设为第一次)。
则对任意u和v,不妨设H[u]≤H[v](否则交换u和v),只要在L中找到[H[u],H[v]]中最小值的下标i,则E[i]就是u和v的LCA。注意到L满足约束RMQ的条件(相邻元素差的绝对值为1),这说明原来的LCA问题已经被转化为约束RMQ。转化过程显然能在O(N)时间内完成。 约束RMQ的解法
现在仍旧用A[0,N-1]表示问题中的数列,这里有|A[i]-A[i-1]|=1(i=1,2,...,N-1)成立。
将A分解为长度为l=[(log N)/2]的块。设A'[i]为第i块中的最小值,B[i]为该最小值的位置。A'[i]和B[i]的长度均为N/l, 所以用ST算法处理A'数组的时空复杂度均为O(N/l*log(N/l))=O(N/logN*(logN-logl))=O(N)。预处理之后,对任意多连续的块进行的查询都能在O(1)时间内实现。余下的问题是如何进行块内查询。
注意到对任意一块中的块内查询的结果有影响的唯一因素是块内每相邻两个元素间的“升降关系”构成的序列。因为每两个元素之间的关系只有两种(“+1”、“-1”),而块的长度又只有l=[(log N)/2],所以本质不同的块最多有2^I=O(sqrt N)种。对每种块中所有可能的块内查询预处理出答案的时空复杂度是O(sqrt N*l^2)=O(N)(这里的O(N)表示不超过线性时间)。预处理出所有块的“类型”,并用二进制数存储的时间复杂度是O(N)。
此后,每次查询可以分为两种情况:
1、块内查询,答案已经被预处理出,只要在数组中找到它即可。
2、块间查询,可以分解为2个块内查询,和一个A'上的RMQ,三者的时间复杂度都是O(1)。
综上,我们给出一个预处理时间为O(n),查询时间为O(1)的在线RMQ算法。 代码
RMQ(Range Minimum/Maximum Query)问题是求区间最值问题。你当然可以写个O(n)的(怎么写都可以),但是万一要询问最值1000000遍,估计你就要挂了。这时候你可以放心地写一个线段树(前提是不写错)O(logn)的复杂度应该不会挂。但是,这里有更牛的算法,就是ST算法,它可以做到O(nlogn)的预处理,O(1)地回答每个询问。 ST算法,Pascal,程序片段 Read(n,q);
For i:=1 to n do Read(d[i,0]);
for j:=1 to Trunc(Ln(n)/Ln(2))do
for i:=1 to n-1 shl j+1 do
d[i,j]:=Max(d[i,j-1],d[i+1shl(j-1),j-1]); for i:=1 to q do begin
Read(a,b);
k:=trunc(Ln(b-a+1)/Ln(2));
rmq:=Max(d[a,k],d[b-1shlk+1,k]); writeln(rmq); end;
分块算法,Pascal,完整过程
var p,n,m,z:longint;
aa,data:array[0..100001]of longint; procedure myread; var i:longint; begin
read(n,m);z:=trunc(sqrt(n)); for i:=1 to n do read(aa[i]); data[1]:=maxlongint;p:=1; for i:=1 to n do begin
if aa[i] begin inc(p); data[p]:=maxlongint; end; end; end; procedure main; var tt1,tt2,a,b,i,j,t1,t2,min:longint; begin for i:=1 to m do begin read(a,b); min:=maxlongint; t1:=a div z+1; t2:=b div z+1; tt1:=a mod z; tt2:=b mod z; if t1=t2 then begin for j:=a to b do if min>aa[j] then min:=aa[j]; write(min,'');continue; end; for j:=(t1-1)*z+tt1 to t1*z do if min>aa[j] then min:=aa[j]; for j:=(t2-1)*z to (t2-1)*z+tt2 do if min>aa[j] then min:=aa[j]; for j:=t1+1 to t2-1 do if min>data[j] then min:=data[j]; write(min,''); end; end; begin myread; main; end. 建立笛卡尔树,C++ void computeTree(int A[MAXN],int N,int T[MAXN]) //T[i]储存每个结点的父结点(左右子树是无所谓的) { Int st[MAXN],i,k,top=-1; //从空栈开始 //第i步,我们将A[i]插入栈中 for(i=0;i //找到第一个小于等于A[i]的元素 k=top; while(k>=0&&A[st[k]]>A[i]) k--; //如上述,更改树的结构 if(k!=-1)T[i]=st[k]; if(k //将A[i]插入栈中,并移除所有更大的元素 st[++k]=i; top=k; } //栈中的第一个元素就是树根,没有父节点 T[st[0]]=-1; }
正在阅读:
RMQ01-13
安全生产工作计划02-28
《统原》自编练习题库01-01
好家风伴我成长作文600字07-16
5AM1U3 小练习03-31
我帮妈妈包饺子作文300字07-10
2017年青岛大学商学院859管理学(2)考研强化模拟题05-01
《公司登记(备案)申请书》03-09
lightcycler 480 Software1.5 中文说明书04-28
圣诞晚会流程安排06-27
- exercise2
- 铅锌矿详查地质设计 - 图文
- 厨余垃圾、餐厨垃圾堆肥系统设计方案
- 陈明珠开题报告
- 化工原理精选例题
- 政府形象宣传册营销案例
- 小学一至三年级语文阅读专项练习题
- 2014.民诉 期末考试 复习题
- 巅峰智业 - 做好顶层设计对建设城市的重要意义
- (三起)冀教版三年级英语上册Unit4 Lesson24练习题及答案
- 2017年实心轮胎现状及发展趋势分析(目录)
- 基于GIS的农用地定级技术研究定稿
- 2017-2022年中国医疗保健市场调查与市场前景预测报告(目录) - 图文
- 作业
- OFDM技术仿真(MATLAB代码) - 图文
- Android工程师笔试题及答案
- 生命密码联合密码
- 空间地上权若干法律问题探究
- 江苏学业水平测试《机械基础》模拟试题
- 选课走班实施方案
- 乡镇社保所怎么改革
- 浅看平行进口
- 河北省物价局文件
- 2014年山东临沂中考满分作文:那一段逝去的时光
- 电缆桥架安装专项施工方案 - 图文
- 江苏省无锡市2016 - 2017学年高二语文上学期期末考试试题 - 图文
- 医学考博--普外专业真题系列
- 萧山区教坛新秀文件
- 机械设计基础第七章
- 循环小数教学设计 教后反思 评课
- 反风演习方案、措施、报告
- 《血站质量管理规范》考核试题
- 三相桥式全控整流及有源逆变电路实验
- 《课堂语言及板书设计》第02章在线测试
- Juniper防火墙日常维护
- 学习使用天平和量筒课时教学设计
- 落实小事德育 狠抓习惯养成 全力提高德育实效
- 三基模拟--外科综合
- 《新编英语教程》第四册汉译英答案(10-12-13)单元
- 高平市赵庄煤矿安全生产灾难事故应急救援预案完整的