RMQ总结

(部分语句来自网络)

RMQ问题(Range Minimum/Maximum Query)即快速最值查询,就是给你一个长度为n的序列A,再给出m个查询,要求输出A序列中(l,r)中的最值。

对于这个问题,提供以下几种算法:

1.朴素(暴力大法好)

所谓暴力自然是每次查询都从l遍历到r,取最值

复杂度  :预处理(读入)为  O(n),查询 为 O(m*n)。

2.线段树(来自高级数据结构的神奇力量)

会线段树的自然不需要多讲,不会线段树的。。。可以先去看看线段树,反正迟早要学(怎么连线段树都不会,丢人,退群吧。

复杂度 :预处理(建树)为 O(n),查询为 O(m*logn)。

注:线段树有明显的优点,即可以在线更新序列A!!!,但线段树的缺点也很明显,就是常数太大。如果只是离线查询并且数据较大的话,还是推荐以下两种(或将问题转为只是离线查询)。

3.ST(RMQ时,动态规划与二分法更配哦)

前面都是小打小闹,接下来才是正题之一(敲黑板!!!)

ST(Sparse Table),实质就是dp+二分法。

我们定义f(i,j)为在序列A中,从i开始连续的2^j个数中最小的数,由显然法可得f(i,0)=A[i],则初始化完成。

接下来进行状态转移,只要j>0,那么我们的个数肯定是偶数,则可以把其分为两个长度为2^(j-1)的区间(i,i+2^(j-1)-1)和(i+2^(j-1),i+2^j-1),转为我们定义的状态分别为f(i,j-1)与f(i+2^(j-1),j-1),则得到:

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

查询的时候,我们就可以把每次要查询的区间分成两个长度为2^k的区间,并取最值就行啦。

注:我们分成的区间可能在中间是会有重叠的,如(2,8)会被拆为(2,5)和(5,8)但是无所谓啦╮(╯_╰)╭,反正不影响结果。则:

 k=(int)(log((double)(r-l+1))/log(2.0));
 ans=min(dp[l][k],dp[r-(1<
这里给出一道例题:

(POJ3264)给你一个长度为n的序列a[N] (1 ≤ N ≤ 50000),询问Q(1 ≤ Q ≤ 200000)次,每次输出【L, R】区间最大值与最小值的差是多少。
裸题,维护两个ST,一个为min,一个为max(其实max只需要初始化时取一个相反数,求最小,在查询时取回来就行了)附代码:
#include
#include
#include
#include
using namespace std;
int n,q,l,r; 
struct ST{
	int dp[50001][17];
	void init()
	{
		for(int j=1;(1<
复杂度:初始化为 O(n*logn),查询为 O(m);
4.RMQ标准算法(什么?前面都是不标准的?!)
复杂度:初始化为 O(n),查询为 O(m);
暂时不会,会了在更。。。

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