RMQ(range minimum/maximum query)即查询区间最大最小值。

对于求区间最大最小值,我们自然而然就想到了一个O(n)时间复杂度的算法,但是如果询问有很多呢?这样必然超时。当然我们可以用线段树来解,使得每一次查询的时间降到log(n),但是对于RMQ算法,只要我们做了些预处理,之后的查询我们仅需要O(1)的时间。Sparse_Table算法是解决RMQ问题的一类较好的算法,属于一种在线算法,至于什么叫在线什么叫离线,先简单介绍一下。

在线算法:在计算机科学中,一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。

离线算法:在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。例如,选择排序在排序前就需要知道所有待排序元素,然而插入排序就不必了。

简单的概括一下 在线算法就是说程序先把预处理工作做好,对于之后的查询,可以很快给你答复。离线算法就是你先把需求告诉程序,他一次性给你解决。

好了,下面来讲解Sparse_Table算法

1.求最值数组

Sparse_Table算法的预处理就是一个动态规划的思想。

设数组maxn[i][j] 表示给定的数组从下标i开始,长度为2^j的区间最大值(最小值一样)也就是arr[i]----arr[i+2^j-1]这个区间的最大值。

于是我们可以写出这样一个动态转移方程maxn[i][j] = max(maxn[ i ][ j-1 ],maxn[ i+2^(j-1) ][ j-1 ]) 看懂了么?

其实就是把区间【i ,i+2^j -1】分成两段,一段是【i,i+2^(j-1)-1】 和【i+2^(j-1),i+2^j】(一直记住二维数组后面一维表示的是区间的长度2^j)

那么对于maxn[i][j]当j等于0,也就是区间长度为1的最大值显然就有maxn[i][0] = arr[i];

到此我们就可以写出Sparse_Table的预处理部分了

 

  1. void getbestarr(int n)//n为给定的数组的长度  
  2. {  
  3.      int tem = (int)floor(log2((double)n));//因为区间的最长长度是2^tem==n嘛  
  4.    for(int i=1;i<=n;i++)  
  5.         minn[i][0]= maxn[i][0] = arr[i];  
  6.     for(int j=1;j<=tem;j++) //下标从1开始  
  7.      for(int i=1;i+(1<<j)-1<=n;i++)  
  8.     {  
  9.          maxn[i][j] = max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);  //最大值  
  10.          minn[i][j] = min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);  //最小值  
  11.     }  
  12. }  

我们看看这个动态规划方程是怎么求解这个maxn,minn数组的

要求区间长度为2的肯定要先求出区间长度为1的嘛  比如区间[1,2]只要在区间[1,1]   [2,2]中取最值嘛 长度为4的肯定要先算出长度为2的嘛 比如求区间[6,9]的最值只要在区间[6,7] [8,9]中取最值嘛。。。。。。

所以最外层的循环就肯定是区间的长度2^j次方咯 里面的循环应该都看得懂吧。


2.查询

这个最值区间的数组求出来了,下面就是查询了 

比如要查询区间[a,b]的最值  怎么求呢?

注意到我们的最值数组存的都是区间长度为2^k(k=0,1,2,3.....)次方的最值

所以对于区间[a,b] 我们肯定要划分为两个区间长度是2^x  2^y的区间才可以直接利用我们得到的最值数组来求最值嘛

这里有两个未知数不好求,我们可以直接取k,对于k满足a+2^k-1=b  k=log2(b-a+1) (注意这里不是a+2^k=b 还是那句话,始终记得2^k是区间的元素的个数) 那么区间a,b的最大值不就是max(maxn[a][k],maxn[b-2^k+1][k])比如对于区间长度为4的[3,6]求出k==2 于是最大值就是区间max(【3,6】,【3,6】)当然我们不能能保证log2(a-b+1)就一定能得到一个整数,但是这不要紧,比如对于区间长度为 5的【3,7】我们对log2(7-3+1)取整得到2,于是最大值就在

max(【3,6】,【4,7】),max函数里面前面那个maxn[a][k]就保证了我们的求最值的区间以a开始,后面那个maxn[b-2^k+1][k]就保证了我们必然能够以b为尾

 

    1. int query(int a,int b,bool getwhat)//getwhat表示你是想取最大还是最小  
    2. {  
    3.    int k = log2(b-a+1);  
    4.    if(getwhat)  
    5.    return max(maxn[a][k],maxn[b-(1<<k)+1][k]);  
    6.    else  
    7.      return min(minn[a][k],minn[b-(1<<k)+1][k]);  
    8. }  

       

posted @ 2014-11-19 22:35  yyxayz  阅读(3945)  评论(0编辑  收藏  举报