rmq入门
对于求区间最大最小值,我们自然而然就想到了一个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的预处理部分了
我们看看这个动态规划方程是怎么求解这个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 l,int r) // 这里返回的是最大值减去最小值 { int k=log2(r-l+1); int maxtemp=max(mx[l][k],mx[r-(1<<k)+1][k]); int mintemp=min(mn[l][k],mn[r-(1<<k)+1][k]); return maxtemp-mintemp; }
简单梳理一下 ,st预处理利用二进制的思想把区间划分为两部分降低复杂度。
由于记录的都是2的n次方长度的最值,对于一般的区间最值我们还需要做一些简单的处理来达到目的
poj 3264
#include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <algorithm> using namespace std; int a[100010]; int mx[100010][80],mn[10010][80]; void st(int n) { int term=(int)floor(log2(double(n))); for(int i=1;i<=n;i++) mn[i][0]=mx[i][0]=a[i]; for(int j=1;j<=term;j++) { for(int i=1;i+(1<<j)-1<=n;i++) { mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]); mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]); } } } int query(int l,int r) { int k=log2(r-l+1); int maxtemp=max(mx[l][k],mx[r-(1<<k)+1][k]); int mintemp=min(mn[l][k],mn[r-(1<<k)+1][k]); return maxtemp-mintemp; } int main() { int n,q; scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) scanf("%d",&a[i]); st(n); while(q--) { int l,r; scanf("%d %d",&l,&r); cout<<query(l,r)<<endl; } return 0; }