RMQ问题之Sparse_Table算法

RMQ问题,全名(Range Minimum/Maximum Query),是求给定区间中的最值问题。

主要方法及复杂度如下:

1、朴素(即搜索),O(n)-O(qn) online。

2、线段树,O(n)-O(qlogn) online。

3、Sparse_Table(实质是动态规划),O(nlogn)-O(1) online。

4、RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(1) online。

前一段时间刚刚学了第三种,ST算法。

ST算法可以在O(nlogn)的预处理以后实现O(1)的查询效率,从而解决查询次数很多(如大于100万)的RMQ问题。


首先,是预处理。预处理是采用dp的思想,我们用f[i][j]表示区间[i,i+2^j-1]中的最大值(即从i开始,长度为2^j的闭区间)。

开始时,f[i][0]一定等于num[i]。好了,初始值找到了,下面是状态转移方程:

f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])。即把[i,i+2^j-1]区间分成两部分[i,i+2^(j-1)-1]和[i+2^(j-1),i+2^(j-1)+2^(j-1)-1],正好和原区间一致。

有了初始值和转移方程,我们可以自底向上递推出所有的f[i][j]的值。

对于边界,还要注意一点。由于区间长度最大为n,所以二维边界最大为log(n)/log(2.0);

一维边界只要满足对于每个起始点,都可以有长度找到n就行了,也就是让i+2^j-1<=n就好了。


然后就是查询了。假设要查询区间[a,b]的最大值,由于区间的长度很可能不是2的整数幂,所以我们要把区间划分为长度为2的整数幂的两部分,而且这两个区间的并集必须是[a,b]。为了实现这个方案,我们需要先求出一个最大的k,使得2^k<=(b-a+1),这样就可以把区间分成两部分[a,a+2^k-1]和[b-2^k+1,b],使他们既能不超过a,b区间的范围,又能把区间全部覆盖。于是,[a,b]区间的最大值就等于上述两个区间的最大值中最大的那个。(有点绕)

弱弱地把NYOJ119题代码贴上,供大家批评指正。原题链接点击打开链接

#include <stdio.h>
#include <string.h>
#include <math.h>
int f[100005][20],v[100005],g[100005][20],N;           //20不一定是唯一的。需要计算log(N)/log(2) 
int max(int a,int b)
{
	return a>b?a:b;
}
int min(int a,int b)
{
	return a<b?a:b;
}
void rmq()
{
	int i,j;
	for(i=1;i<=N;i++)
	{
		f[i][0]=v[i];
		g[i][0]=v[i];
	}
	int sb=(log((double)(N))/log(2.0));
	for(j=1;j<=(int)(log((double)(N))/log(2.0));j++)
	{
		for(i=1;i+(1<<j)-1<=N;i++)
		{
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
			g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]);
		}
	}
}
int main()
{
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	memset(v,0,sizeof(v));
	int Q,m,n,i,k;
	scanf("%d%d",&N,&Q);
	for(i=1;i<=N;i++)
		scanf("%d",&v[i]);
	rmq();
	while(Q--)
	{
		scanf("%d%d",&m,&n);
		k=log((double)(n-m+1))/log(2.0);
		int sb=int(k);
		int dp_min=min(g[m][k],g[n-(1<<(k))+1][k]);
		int dp_max=max(f[m][k],f[n-(1<<(k))+1][k]);
		printf("%d\n",dp_max-dp_min);
	}
	return 0;
}        


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值