一.什么是RMQ问题
RMQ(Range Minimum/Maximum Query),即区间最值查询问题。给你一组数据若干个查询,在短时间内回答每个查询[ l ,r ] 内的最值。
二.RMQ问题解决策略
(1)朴素搜索 : 暴力(DFS/BFS/搜索)
(2)线段树 : 线段树浅谈
(3)ST动态规划算法 : 本文(动态规划思想)
(4)笛卡尔树标准算法 : 待补
三.ST动态规划算法
1.什么是ST算法
这里介绍了一种比较高效的在线算法(ST算法)解决这个问题。所谓在线算法,是指用户每输入一个查询便马上处理一个查询。该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询。ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。
2. 算法思想
(1)预处理
<1>这里应用了一个很重要的思想:我要求[ l, r ]里面的最值,我只要取[ l , x ] 中的最值和 [ x +1 , r ]中的最值中较大/小的一个就是 [ l,r ] 里面的最值,这就是动态规划的思想。
<2>我们用F[ i , j ]表示从i开始,连续(2^j)个数字中的最值。
这里为什么用2^j呢?
因为一方面我们要把区间等分,那么区间长度肯定是为偶数。
另一方面,2^x可以组合为任何长度。
<3>由(1)可知:我们要把 2^j 等分 , 所以F[i , j] 对应区间为 [ i , i + 2^j - 1 ];等分后为 [ i, i + 2^(j-1) - 1] 和 [ i + 2^(j-1) , i + 2^j - 1 ];两区间内数字个数都为 2^(j-1)个,所以:
初态:F[ i ,0] = A[i] ;
递推公式: [i , j] = max( [ i , 2^(j-1) ] ,[ i + 2^(j-1) , 2^(j-1) ] ) 即 F[i , j] = max( F[ i , j-1 ] ,F[ i + 2^(j-1) , j-1 ] )
(2)查询
我们要查询[ l ,r ] 内的最值怎么办呢?因为区间长度(r - l + 1)可能并不正好是2^n,所以我们这里可以重复取最值。
比如: 查询[ 5 , 6 , 8 , 1, 10] 也就是 查询 max( [ 5 , 6 , 8 ,1] , [ 6 , 8 , 1 , 10]),可以重复!避免遗漏。
取k = log2(r - l + 1); F[l,r] = max( F[ l , k] , F[ r - 2^k + 1 , k ] )
法一:int k = (int)(log(r - l + 1.0)/log(2.0));
法二:int k = 0; while((1<<(k+1))<=(r - l + 1))k++;
3.代码(查询区间最大/最小)
#include <iostream>
#include<bits/stdc++.h>
const int maxn = 100000 + 7;
using namespace std;
int Max[maxn][20],Min[maxn][20];
int n,m;
void init(){
for(int j = 1;(1<<j)<=n;j++){
for(int i = 1;i + (1<<j) - 1<=n;i++){
Max[i][j] = max(Max[i][j-1],Max[i + (1<<(j-1))][j-1]);
Min[i][j] = min(Min[i][j-1],Min[i + (1<<(j-1))][j-1]);
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i = 1;i<=n;i++){
int num;
scanf("%d",&num);
Max[i][0] = Min[i][0] = num;
}
init();
while(m--){
int l,r;
scanf("%d%d",&l,&r);
int k = (int)(log(r - l + 1.0)/log(2.0));//法一
// int k = 0;
// while((1<<(k+1))<=(r - l + 1))k++;//法二
int ans1 = max(Max[l][k],Max[r - (1<<k) + 1][k]);
int ans2 = min(Min[l][k],Min[r - (1<<k) + 1][k]);
printf("%d %d\n",ans1,ans2);
}
}
return 0;
}
//3
//10 3
//1 5 6 0 1000 -12 66 -100 300 99