(部分语句来自网络)
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<<k)+1][k]);
(POJ3264)给你一个长度为n的序列a[N] (1 ≤ N ≤ 50000),询问Q(1 ≤ Q ≤ 200000)次,每次输出【L, R】区间最大值与最小值的差是多少。裸题,维护两个ST,一个为min,一个为max(其实max只需要初始化时取一个相反数,求最小,在查询时取回来就行了)附代码:复杂度:初始化为 O(n*logn),查询为 O(m);#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; int n,q,l,r; struct ST{ int dp[50001][17]; void init() { for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); } inline int query(int l,int r) { int k=(int)(log((double)(r-l+1))/log(2.0)); return min(dp[l][k],dp[r-(1<<k)+1][k]); } }minn,maxx; int main() { //freopen("a.in","r",stdin); scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) scanf("%d",&minn.dp[i][0]),maxx.dp[i][0]=-minn.dp[i][0]; minn.init();maxx.init(); for(int i=1;i<=q;i++) { scanf("%d %d",&l,&r); printf("%d\n",-maxx.query(l,r)-minn.query(l,r)); } return 0; } //POJ3264
4.RMQ标准算法(什么?前面都是不标准的?!)复杂度:初始化为 O(n),查询为 O(m);暂时不会,会了在更。。。
RMQ总结
最新推荐文章于 2023-09-15 16:14:24 发布