ST算法——RMQ问题(区间最值问题)
引子:
RMQ 的英文是 Range Maximum(Minimum) Query,翻译过来其实就是区间求最值的意思。问题描述:对于长度为 n 的数列 A,回答若干询问 RMQ(A, i, j)(i, j <= n),返回数列A中下标在 [i, j] 里的最小(大)值。
因为可能需要海量的去查询操作,如果一个一个去找最大值那么时间复杂度将会巨大!!所以可以采用倍增的思想去提前预处理,这样时间复杂度就会使O(nlogn),然后以O(1)的时间去查询。
~~解释:F[i,j]表示在数列中,子区间在[i,i+2^j-1] 里面的最大值,也就是从i开始的2的j次方个数里面的最大值。递推边界时F[i,0]=a[i],即[i,i]的最大值。
在递推时我们把子区间的长度成倍增长,有公式F[i,j]=max(F[i,j-1],f[i+1<<(j-1),j-1],即长度为2的j次幂的子区间的最大值是左右两半长度为2^(j-1)的子区间的最大值中较大的一个
代码如下:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int a[1e6+5],f[1e6+5][20];
void ST_prework(int n)
{
for(int i=1;i<=n;i++) f[i][0]=a[i]; //f[i][j]表示在区间[i,i+2^j]最大值
int t=log(n)/log(2)+1;
for(int j=1;j<t;j++) //二进制倍增:主题是以二进制倍增的思想
for(int i=1;i<=n-(1<<j)+1;i++) //将长度为2^j的子区间的最大值是左右两半子区间长度为2^(j-1)的子区间的最大一个
f[i][j]=max(f[i][j-1],f[i+1<<(j-1)][j-1]);
}
int ST_query(int l,int r)
{
int k=log(r-l+1)/log(2);
return max(f[l][k],f[n-(1<<k)+1][k]); //计算一个k,使得2^k<=r-l+1<2^(k+1)
} //使得2^k小于区间长度得前提下得最大得k,那么从l开始得2^k个数和以r结尾得2^k个数一定比包含了整个区间[l.r]
int main()
{
int n,m;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
cin>>m; //输入访问次数
ST_prework(n); //对数组进行一个预处理,时间复杂度O(nlogn)
while(m--)
{
int l,r;
cin>>l>>r;
cout<<ST_query(int l,int r);
}
return 0;
}
/*RMQ 的英文是 Range Maximum(Minimum) Query,翻译过来其实就是区间求最值的意思。
问题描述:对于长度为 n 的数列 A,回答若干询问 RMQ(A, i, j)(i, j <= n),返回数列A中下标在 [i, j] 里的最小(大)值。
在这个问题中,我们需要关注的是查询操作,查询可能是海量的。
所以如果我们对数据进行快速的预处理,然后在外面处理后的数据结构中进行快速查询,其实就是最理想的状态。
时间复杂度O(nlogn) 然后再以O(1) 时间去在线回答
*/
PS:这算法看了我好长时间,果然什么东西还是得动手去写代码,光看是没用的,还是得看你去写然后去理解这样才能真正去理解!!