RMQ问题的几种解决方案

来源:互联网 发布:淘宝女装排名 编辑:程序博客网 时间:2024/05/21 15:38

RMQ作为学习Suffix Array的基本知识之一,我想在写SA总结之前写下这一部分的知识。

RMQ问题可以算是经典问题中的经典,目的就是希望用较短的时间完成求区间最小值的操作。

方法有很多种,最简单的直接线性统计的效率极其低下,也比较白痴,差不多都应该会。

下面讲下几个比较好的RMQ问题的算法。

方法一:线段树

线段树作为维护区间性质的最佳利器,绝对是不错的选择。写起来可以递归,非常的方便。

但是他也是有缺点的,最大的问题就是虽然说是O(nlogn)-O(logn)的时间复杂度中的常数比较大。从实际运行效率上看不是很好。

我前面的日志里面写过一些线段树的题,线段树的用法大同小异,也比较好学,这里不再赘述。

方法二:RMQ的ST算法

ST算法是一个很不错的算法,复杂度为O(nlogn)-O(1)。这种算法运用的主要思想是倍增思想。

倍增思想的核心就是,通过递推使得结论成倍地提高。

这里我们定义这样一个数组f[i,j]表示从i开始向后数2j个元素的RMQ

递推方程就是

           f[i,j]:=min(f[i,j-1],f[i+1 shl (j-1),j-1]);

边界:f[i,0]:=a[i];

这样我们就可以用这样的式子来解决RMQ问题:

设k:=trunc(ln(j-i+1)/ln(2))

          RMQ(i,j):=min(f[i,k],f[j-(1 shl k)+1,k]);

对于某一些特殊的RMQ问题还有O(n)-O(1)的算法,方法在于分成长度特殊的段,然后再用这些段RMQ。这是将LCA问题转化为RMQ问题的途径。

方法三:笛卡尔树

这个方法的精髓在于将RMQ问题转化为LCA问题。

笛卡尔树的定义是,对于一个数组A来说,笛卡尔树的根的编号是这个数组的最小值的所在的编号k。他的左子树就是在数组A[1..k-1]上的一棵笛卡尔树,右子树是一棵在数组A[k+1..n]的一棵笛卡尔树。

这样RMQ(A,i,j)就被转化成了LCA(T,i,j)。

因为LCA问题离线Tarjan算法的复杂度是O(n)的,所以这个问题最终的复杂度在O(n)。

我的理解就是理论性比较强,并没有什么实际意义。

缺点是不支持在线提问。

 

以上就是RMQ问题的几种经典方法,如果想要透彻的理解好线段树和倍增法,还是需要一些题目的磨练。

推荐的一个题是MIPT上的105号MRQ Problem。数据真的是很强,有很多地方要注意,用ST时候要格外注意内存。

另外用P的同学,请使用Kylix编译器提交,否则这辈子估计过不了了啊……

线段树的代码以前贴过,这里给下我的ST算法的代码: