RMQ(Range Minimum/Maximum Query)区间最值查询,即给出长度为n的数组A,以及m组询问s、t(s<=t<=n),返回区间[s,t]中的最值。

用这篇替换掉了以前那篇Naive、漏洞百出的旧博文。


·O(mn)的暴力做法

int solve(int s, int t){  int ans=-INF;  for(int i=s;i<=t;i++)  ans=max(ans,a[i]);  return ans;}

但这样的复杂度在数据很大的情况下,显然是不足取的。


·稀疏表(Sparse Table)

可以利用倍增的思想,通过稀疏表来解决这个问题。

eg.以数组a={3 2 4 5 6 8 1 2 9 7}为例 

用F[k,i]表示从第i个起连续2^k个数中的最大值
那么显然F[0,x]=x
于是有F[1,1]=max(3,2)=3,F[1,3]=max(4,5)=5
而对于F[2,1],则F[2,1]=max(F[1,1],F[1,3])=max(max(3,2),max(4,5))=max(3,5)=5

由此可得:

初值 F[0,i]=a[i]
递推方程 F[k, i]=max(F[k-1,i], F[k-1,i + 2^(k-1)])
 

预处理

类似于DP,时间复杂度为O(nlogn)。代码如下:

void init(){   for(i=1; i<=n; i++) f[0][i] = a[i];   for(int k = 0; k <MAXLOGN; k++)   for(int i = 1; i <=n; i++){     f[k+1][i]=max(f[k][i],f[k][i+(1<<k)]);  }}  


查询
对于待查询区间[s,t],首先需要找到覆盖这个区间的最小幂区间,其长度可知是t-s+1,取k=log2(s-t+1),那么答案就是max(f[k,s],f[k,t-2^k+1]),其中两个区间互相重叠的部分不影响答案。每次查询只需要O(1)的时间复杂度。
代码如下:
int query(int i, int j){    int k = (int)(log(t-s+1)/log(2));    return max(f[k][s], f[k][t-(1<<k)+1]);}

·线段树

网上资料挺多的,自行搜索看看吧,本蒻也不熟练,嘿嘿。

建树O(n),查询O(logn),相比ST,适合用于n更大,m较小的情况。

这里贴上一段通俗易懂普普通通的完整代码:

#include<cstdio>#define max(a,b) ((a>b)?a:b)const int MAXN = 200005;const int INF = 1e9;int n,m;int a[MAXN],t[MAXN*3];void build(int node = 1, int l = 1, int r = n){     if (l==r) t[node] = a[l]; //到叶子上,则赋值     else{        build(node*2, l, (l+r)>>1);  //左儿子        build(node*2+1, ((l+r)>>1) + 1, r);  //右儿子        t[node] = max(t[node*2], t[node*2+1]); //回溯赋值     }}int query(int s, int t, int node = 1, int l = 1, int r = n){ //查询区间[s,t],当前查询结点的位置为node,所表示的区间为[l,r],默认node为根结点     if(l>t||r<s) return -INF; //当前区间与所查询区间无交集,返回一个不影响答案的值     if(l>=s&&r<=t) return t[node];  //当前区间包含于所查询区间,直接返回当前区间的最值就好了     int vl = query(s, t, node*2, l, (l+r)>>1); //查询左儿子     int vr = query(s, t, node*2+1, ((l+r)>>1) + 1, r); //查询右儿子     return max(vl, vr);}int main(){   scanf("%d", &n);     for(int i = 1; i <= n; i++)      scanf("%d", &a[i]);   build(); //建树   scanf("%d", &m);   for(int i = 1; i <= m; i++){      int s,t;      scanf("%d%d", &s, &t);      printf("%d\n", query(s,t));   }   return 0;}