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

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;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值