POJ3264 Balanced Lineup 【RMQ问题的四种解法】

来源:互联网 发布:网络编辑工资多少 编辑:程序博客网 时间:2024/06/05 01:04

Problem Address:http://poj.org/problem?id=3264

 

【前言】

 

RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。

 

特别的,在这道题中,要求某个区间内最大值与最小值之差。

 

本来是在学习线段树的,拿到这个题目,很快就写好了。

 

但是发现这是一个赤裸裸的RMQ问题。而事实上RMQ问题还有一种ST算法的应用。

 

最后掀开了收藏夹中一篇封尘的用树状数组解决RMQ问题的网页。

 

最后总结出了四种解决RMQ问题的方法,并在空间、时间以及编程复杂度方面作出了比较。

 

 

 

【方法一:朴素算法】

 

最朴素的方法莫过于朴素模拟。

 

用一个数组记录所有数据。

 

对于每次询问,简单地遍历一次该区间,找出最大最小值,求差。

 

这种朴素方法的编程复杂度极低,空间复杂度为O(n),初始化时间复杂度O(n),查询时间复杂度为O(n)。

 

这个时间复杂度在这个题目中是无法接受的,因为n=50000,m=200000。

 

如果对于小型的数据,这样的算法还是可以接受的。

 

在这里也就不贴出实现的代码了。

 

 

 

【方法二:线段树】

 

相信很多人对线段树都是不陌生的。

 

线段树在区间和点的操作上都是很有优势的。

 

如果把线段树用于朴素的RMQ问题,那么想当于是用了二分的思想:

 

如果刚好吻合,则返回最值;如果区间位于左边,则向左搜索;如果位于右边,则向右搜索。如果跨越了左右,则分两边搜索并取最值者。

 

线段树的思想还是比较简单的。

 

但是,线段树的编程复杂度在四者中是最大的,代码最长。空间复杂度为O(n),初始化时间复杂度为O(nlgn),查询时间复杂度为O(lgn)。

 

这个查询复杂度O(lgn)跟上面朴素算法O(n)相比可谓是天壤之别。由于有m个查询,这个差距是显而易见的。

 

实现代码见文章末尾。

 

 

 

【方法三:ST算法】

 

ST算法可谓是RMQ问题的经典算法。

 

它的编程复杂度算是比较低的。空间复杂度为O(nlgn),初始化时间复杂度为O(nlgn),查询时间复杂度为O(1)。

 

O(1)是什么概念,O(1)就是,初始化之后,你一输入一个查询,马上就可以得到一个结果。

 

事实上,ST算法就是个动态规划。用一个二维数组dp[i][j]存储从i开始的2^j长度中的最值。

 

如果你想查询一个区间(l, r)的最值,只需要找到一个2^k的长度使(l, l+2^k)和(r-2^k+1, r)覆盖到整个区间,然后取最值就得出结果。

 

但是查询时间上巨大的优化也为其空间上带来了更多的劣势。

 

一般情况下ST算法会占用更多的内存。

 

如此算来,在RMQ问题上,相比于线段树,ST算法毫无优势可言。

 

实现代码见文章末尾。

 

 

 

【方法四:树状数组】

 

这种方法其实一旦理解了就会发现它是很简单的。

 

跟用树状数组求解区间和问题类似,这里也是用同样的方法构造了一个新的数组存储最值。

 

查询的时候从后向前查。

 

如果待查区域包含了管辖区域,则读取该管辖区域的最值;

 

如果是被包含的关系,则只读取当前位置的数据,使索引后减一并重复上述步骤直到全部查询完。

 

这种思路是很简洁的,实现起来也很简洁。

 

树状数组相比于线段树是有很多优势的,包括它的编程复杂、空间复杂度和时间复杂度。

 

虽说树状数组的功能线段树都能实现,而线段树的功能树状数组不一定能实现,但是如果可以的话用树状数组是一个非常不错的选择。

 

在这里,树状数组的空间复杂度为O(n),初始化时间复杂度为O(n),查询时间复杂度为O(lgn)。

 

虽然线段树和树状数组都是O(n)的空间复杂度,但是前者的系数要比后者的大得多。

 

 

 

【比较总结】

 

在这个问题中,综合多方面的考虑,树状数组可以说是最好的选择。

 

以下为三者的比较:

 

                Memory             Time           Code Length

 

线段树         2724K            2172MS             1730B

 

ST算法        8232K            1985MS             1210B

 

树状数组      760K              1500MS             1277B

 

 

虽然可能会因为个人的编程方式不同而在各个数据之间有所差异,但是就整体来说,这组数据还是多少能反映点问题的。

 

树状数组因为其简洁与方便而著称,如果能够巧妙地转化问题,则可以达到一个不错的效果。

 

但是,线段树的应用是很广泛也很灵活的,不能因为它的编程复杂度高而放弃了对它的选择。

 

而关于ST算法,与其说是一种算法,还不如说是一种思想,动态规划的思想,而ST只是它的一个应用而已。

 

 

 

【代码】

 

(代码一:线段树)

 

 

(代码二:ST算法)

 

 

(代码三:树状数组)

 

 

 

 

【P.S】

 

我承认很多代码都是抄过来的。包括一些牛人的博客,当然还有很多很多介绍类似东西的网页。

 

其实我也是处于一个学习的阶段,还有很多需要学习的东西,模仿也在所难免。

 

我也希望能够在“抄袭”的过程中领悟到学习的魅力,继续坚持着自己的道路走下去。

 

 

 

【完】

 

原创粉丝点击