一.简介
RMQ是英文Range Maximun(Minimum) Query 的缩写,顾明思义是询问某个区间内的最大值或最小值。下面讲解RMQ的求解方法-ST算法。
ST算法通常用在多次询问一些区间的最值的问题中。相比于线段树,它的程序实现更简单,运行速度更快,它可以做到O(nlogn)的预处理,O(1)回答每个询问。
使用ST算法的条件是没有修改操作,因此它适用于没有修改操作并且询问次数多(10^6级别甚至更大)的情况。
二.ST算法流程
1.预处理
ST算法的原理实际上是动态规划,我们用a[1...n]表示一组数。设f[i,j]表示从a[i]到a[i + 2^j - 1]这个范围内的最大值,也就是以a[i]为起点连续2^j个数的最大值。由于元素个数为2^j个,所以从中间平分成两部分,每一部分的元素个数刚好为2^(j-1)个,也就是说,把f[i , j]分为f[i,j - 1]和f[i + 2^(j-1) , j - 1],如图所示
整个区间的最大值一定是左右两部分最大值的较大值,满足动态规划的最优化原理分析得到状态转移方程:f[i][j]=max(f[ i ][j - 1],f[ i + 2^(j-1) ][ j - 1 ]),边界条件为f[i][0]=a[i],这样就可以在O(nlogn)的时间复杂度内预处理f数组。
2.询问
若我们要询问区间[ l, r] 的最大值,则先求出最大的x满足2^x <=r-l+1,那么区间[ l , r ]=[ l , l + 2^x - 1] U [ r - 2^x + 1 , r]。
两个区间的元素个数都为2^x ,所以[l , r] 的最大值为max(f[l][x],f[r - 2^x + 1][x]) ,可以在O(1)内计算出来。虽然这两个区间有交集,但是对于求区间最值来说没有影响,这就是ST算法只适用于求区间最值的原因。
求区间[x , y] 最大值,直接给出表达式:
k=log2(y-x+1);
ans=max(f[x][k],f[y - 2^k + 1][k])
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define MAX 1000005
#define N 30
int Log[MAX],ans[MAX][N],num[MAX];
int n,que,x,y;
int main()
{
int i,j,c,T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&num[i]);
}
Log[0]=-1;
for(i=1;i<=n;i++)
{
ans[i][0]=num[i];
Log[i]=Log[i>>1]+1;
}
for(j=1;j<=N;j++)
{
for(i=1;i+(1<<j)-1<=n;i++)
{
ans[i][j]=max(ans[i][j-1],ans[i+(1<<j-1)][j-1]);
//若求最小值就是min()
}
}
scanf("%d",&que);
while(que--)
{
scanf("%d%d",&x,&y);
int temp=Log[y-x+1];
printf("%d\n",max(ans[x][temp],ans[y-(1<<temp)+1][temp]));
}
}
return 0;
}
摘自《信息学奥赛一本通 提高篇》