RMQ即范围最小值问题(Range Minimum Query,RMQ)
用以解决某段范围内最小值的问题(最大值也可以)
其预处理效率为O(nlogn)查询效率O(1)是一个非常小的常数
RMQ的核心思想在于运用一个二维数组来储存一段长度为2^k范围的最小值
即D[i][j]表示从i开始长度为2^j范围内的最小值
那么我们按照递推的一个思路可以得出
D[i][j]=min(D[i][j-1],D[i+2^(j-1)][j-1])
因为2^j小于n 所以D[][]的大小为D[n][logn] 所以预处理时间为O(nlogn) 空间复杂度也为O(nlogn)
(这里开空间之前建议先写一个小程序预先计算一下logn的最大值 再开空间)
预处理代码如下
<p>inline int MIN(int a,int b)
{
return a<b?a:b;
}</p><p>
inline void RMQ_init()
{
int i,j;
for (j=1;(1<<j)<=n;j++)
{
for (i=0;i+(1<<j)-1<n;i++)
{
d[i][j]=MIN(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
}
}</p>
接下来是查询 我们只需要找到两个区间 使得两个区间覆盖了题目要求的区间即可 因为是求最值所以即便是有重复也没有关系(如果是累加则不允许出现重复)
因此我们现在O(1)的时间复杂度里面需找一个k值 使得k是满足2^k<=R-L+1的最大整数
那么D[L][k]与D[R-2^k+1][k]的较小值就是所要求的
查询代码如下
inline int RMQ(int L,int R)
{
int k=0;
while((1<<(k+1))<=R-L+1)
k++;
return MIN(d[L][k],d[R-(1<<k)+1][k]);
}
这里注意L和R是从下标0开始算的 如果题目给出1开始算下标 那么应该计算RMQ(L-1,R-1)
接下来以SOJ2436 Picture puzzle game为例 试用一下RMQ
问题描述 fzk特别擅长拼图游戏,作为科大ACM/ICPC代表队STUDENT的主力成员,他每逢大赛前必然要买一个新的 拼图来玩拼图游戏,据说可以攒人品哦!所以迄今为止,fzk已经玩过了好多不同的拼图游戏,我们可 以记作 Game1,Game2,Game3,……,GameN。每次他都会记录下来完成拼图的时间,分别是 Time1,Time2,Time3,……,TimeN(单位:分钟)。gzsun十分嫉妒fzk的拼图能力,所以往往故意刁难fzk ,问他:“老范,你好年轻啊,hiahia~~。那么从Gamei到Gamej,你完成一个拼图用的最少时间是多少 啊?”这下可难坏了fzk,你能不能帮助fzk回答gzsun呢? 问题输入 首先一个整数t表示测试数据组数(1=<t<=10)。对每组数据,第一行是一个整数N(0<N<=30000),表 示总共玩的游戏数。接下来的的一行中是N个整数,对应于Time1…TimeN(都是不超过1000000000的正整 数)。然后是一个整数M(0<M<=30000),表示gzsun的询问次数。接下来的M行中每行有两个正整数i,j ,1<=i<j<=N。 问题输出 对每组测试数据中的每次询问(i,j),输出一个正整数t,使得t = min{Timek, i<=k<=j}。 样例输入 1 4 5 3 7 2 3 1 4 2 3 4 4 样例输出 2 3 2
分析 标准的RMQ模板题 也可以用线段树解决 不过代码量会相对多一些 这里题目给出的n最大为30000 为了防止MLE这里我先写了个小程序计算了一下log30000 大约是17左右
为了拿到best solution的no.1 这里也使用了getchar代替scanf读数 将速度提升到极致
代码如下:
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
const int maxN=30005;
int n;
int A[maxN];
int d[maxN][17];
inline bool get(int &t)
{
bool flag = 0 ;
char c;
while(!isdigit(c = getchar())&&c!='-') if( c == -1 ) break ;
if( c == -1 ) return 0 ;
if(c=='-') flag = 1 , t = 0 ;
else t = c ^ 48;
while(isdigit(c = getchar())) t = (t << 1) + (t << 3) + (c ^ 48) ;
if(flag) t = -t ;
return 1 ;
}
inline int MIN(int a,int b)
{
return a<b?a:b;
}
inline void RMQ_init()
{
int i,j;
for (j=1;(1<<j)<=n;j++)
{
for (i=0;i+(1<<j)-1<n;i++)
{
d[i][j]=MIN(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
}
}
inline int RMQ(int L,int R)
{
int k=0;
while((1<<(k+1))<=R-L+1)
k++;
return MIN(d[L][k],d[R-(1<<k)+1][k]);
}
int main()
{
int t,i,m,l,r;
get(t);
while(t--)
{
get(n);
for (i=0;i<n;i++)
{
get(d[i][0]);
}
RMQ_init();
get(m);
while(m--)
{
get(l);
get(r);
printf("%d\n",RMQ(l-1,r-1));
}
}
return 0;
}