RMQ问题

系统 1852 0

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 的预处理部分了

 

    void getbestarr(int n)//n为给定的数组的长度

{

     int tem = (int)floor(log2((double)n));//因为区间的最长长度是2^tem==n嘛

   for(int i=1;i<=n;i++)

        minn[i][0]= maxn[i][0] = arr[i];

    for(int j=1;j<=tem;j++) //下标从1开始

     for(int i=1;i+(1<<j)-1<=n;i++)

    {

         maxn[i][j] = max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);  //最大值

         minn[i][j] = min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);  //最小值

    }

}
  

我们看看这个动态规划方程是怎么求解这个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为尾

 

    int query(int a,int b,bool getwhat)//getwhat表示你是想取最大还是最小

{

   int k = log2(b-a+1);

   if(getwhat)

   return max(maxn[a][k],maxn[b-(1<<k)+1][k]);

   else

     return min(minn[a][k],minn[b-(1<<k)+1][k]);

}
  


 

 

RMQ问题


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论