【RMQ】区间求最值 SOJ2436 Picture puzzle game

来源:互联网 发布:100元能干什么知乎 编辑:程序博客网 时间:2024/05/16 01:59

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}。样例输入145 3 7 231 42 34 4样例输出232

 

分析 标准的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;}


 

0 0
原创粉丝点击