1.引例
假如现在有一个问题,给出了一个数组,又给出了$L$,$R$的左右端点,你需要求从$L$到$R$区间的最大值
(1)按顺序排序,直接查询—————————————————————不可以,如果原数组顺序被打乱,则得不到准确结果
(2)遍历每一个点,边枚举边更新最大值—————————————不可以,复杂度非常高,会TLE
(3)利用RMQ中的ST表——————————可以
2.什么是RMQ问题
(1)RMQ问题(Range Minimum/Maximum Query)顾名思义,是一个区间最值问题,通俗来说就是某一段区间的最大或最小值
(2)RMQ问题与ST表的区别:RMQ指的是一类问题,而ST表是求这一类问题的一种具体方法
3.ST表总体思想
ST表这种方法从本质上来说就是一种DP,用了一种叫倍增的思想(你可以暂时知道有这一种思想即可)
既然是DP,最重要的是要有状态转移方程
而在有状态转移方程之前,我们需要用一个二维数组表示状态
我们不妨设状态为f[i][j]
即有:
f[i][j]表示从i端点开始的长度为2的j次方的区间最值(取决问题要求的是最大值还是最小值)
现在我们来考虑怎么来求f[i][j]
我们可以用类似线段树/二分的思想来求f[i][j]
所以有如下状态转移方程:
f[i][j]=max/min(f[i][j-1],f[i+(1<<j-1)][j-1])
看一下代码:
for(int j=0;j<=M;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
if(!j)f[i][j]=a[i];
else f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
注意,这里的循环一定要这么写,因为ST表预处理时先把2的0次方~2的n次方找到,再进行预处理
刚才我们说的是ST表的预处理,现在我们来说一下怎么查询
假如我们有两个端点L,R
我们设这段区间的长度为len(r-l+1)
我们再找一个数k,令2的k次方≤len,则两段2的k次方可以严格覆盖这段区间
所以我们有如下查询的方程
为:
f[i][j]=max/min(f[l][k],f[r-(1<<k)+1][k])
(k为log(len)(以二为底),所以,k为log(len)/log(2)(换底公式,了解即可))
有如下查询的代码
int l,r;
cin>>l>>r;
int len=r-l+1;
int k=log(len)/log(2);
cout<<min(f[l][k],f[r-(1<<k)+1][k])<<endl;
4.代码实现
又双叒叕到了~~喜闻乐见~~的环节
上代码~~~~代码以找最小值为例
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=20;
int n,m;
int a[N];
int f[N][M];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int j=0;j<=M;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
if(!j)f[i][j]=a[i];
else f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
while(m--)
{
int l,r;
cin>>l>>r;
int len=r-l+1;
int k=log(len)/log(2);
cout<<min(f[l][k],f[r-(1<<k)+1][k])<<endl;
}
return 0;
}