RMQ又是区间查询问题:例如,给定一个数组,给你任意段区间,
,求这些区间的最值(可以最大值,也可以最小值)。
解决算法:ST (Sparse - Table算法,基于动态规划求区间最值的算法)
这个算法的核心在于二维数组的预处理作用,由它来表示出状态转移方程。
表示从第
个数开始的
个数(这个区间)的最值。状态转移方程可有长度递增推出来,是下列的形式:
大白话翻译就是从第个数开始的
个数的最大值=
(从第
个数开始的
个数的最大值,从第
位置开始的
个数的最大值),最小值同理。左右两个区间的数的个数合起来正好是
个。
对边界的处理:边界条件为F[i][0]=A[i]。
接下来就是查询,这里,首先这是一个换底公式,所以其实是
,
表示这个区间长度。
还有一点就是为什么RMQ循环里面的外层循环是,而内循环是
。可以这样想,我们每次求
的时候都要保证
已经求过,但是
没有保证
的
是求过的,把
循环放里面,你只有当把这一层所有的
循环都求过了,才可以保证这一点。
代码算是一个模板,我根据https://blog.csdn.net/chenzhenyu123456/article/details/47298867这个博客来的,加上了自己的思考,博文还有降维,即一维数组就搞定了,确实很强,这里我先给出二维的,一维的等我理解到一定程度了再补上!
代码如下:
#include<iostream>
#include<math.h>
#include<algorithm>
#include<string.h>
#define MAXN 10020
#define INF 0x3f3f3f3f
using namespace std;
int A[MAXN];
int N,M;
int Amax[MAXN][50]; //Amax[i][j]表示从i开始的,长度为2的j次方的区间里面的最大值
int Amin[MAXN][50];//Amin[i][j]表示从i开始的,长度为2的j次方的区间里面的最小值
void RMQ()
{
for(int i=1;i<=N;i++)
Amax[i][0] = Amin[i][0] = A[i];
for(int j=1;(1<<j) <= N;j++)
{
for(int i=1;i+(1<<j)-1 <= N;i++)
{
Amax[i][j] = max(Amax[i][j-1],Amax[i+(1<<(j-1))][j-1]);
Amin[i][j] = min(Amin[i][j-1],Amin[i+(1<<(j-1))][j-1]);
}
}
}
int query(char *op,int L,int R)
{
//两种求k的方法
/*int k=0;
while((1<<(k+1)) <= R-L+1)
k++;*/
int k = (int)(log(double(R-L+1))/log((double)2));
if(strcmp(op,"MAX") == 0)
return max(Amax[L][k],Amax[R-(1<<k)+1][k]);
else
return min(Amin[L][k],Amin[R-(1<<k)+1][k]);
}
int main()
{
while(cin>>N>>M&&N&&M)
{
for(int i=1;i<=N;i++)
cin>>A[i];
RMQ();
char s[10];
int x,y;
while(M--)
{
cin>>s>>x>>y;
cout<<query(s,x,y)<<endl;
}
}
return 0;
}
运行结果如下: