把 RMQ算法 讲清楚

RMQ算法即为区间最值查询。
用于解决:
多次查询区间 [ i , j ] 之间的最小值/最大值

运用 dp 的思路 ,
dp [ i ] [ j ] 代表 数组 a [ i ] ~ a [ i + 2 j -1 ] 这个范围内的最大值
状态转移方程 : dp [ i ] [ j ] = max ( dp [ i ] [ j - 1 ] , dp [ i + (1 << ( j - 1) ] [ j - 1 ] )

void RMQ()
{
	int i,j;
	for(i=1;i<=n;++i) f[i][0]=a[i];
	for(j=1;(1<<j)<=n;++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]);
}

(预处理 ↑↑↑↑)复杂度 j 循环是O(log n),i 循环是O(n),总共是O(n * log n)

解释:
范围a[ i ] ~ a [ i + 2 (j-1) - 1] 与
范围a [ i + 2 (j-1)] ~ a [ i + 2 (j-1) + 2 (j-1) ] (即a [ i + 2 j -1 ])合并

把 RMQ算法 讲清楚_第1张图片

例子:
如果要求 [ l , r ] 这个区间的最大值,
只要求出 max ( dp [ l ] [ k ] , dp[ r - ( 1 << k ) + 1 ] [ k ] )即可
这里k = int (log2 (r - l + 1)) ,取整了,
因为k 小于或等于log2 (r - l + 1)其实都没事,
无非就是这两个范围正好全部重叠或者部分有重叠,肯定能求出 [ l , r ] 这个区间的最大值

(查询↑↑↑↑)每次询问的复杂度是O (1) 有 q 个询问就是 O (q)

求最小值也同理

预处理log数组步骤:

log[1]=0;
for(int i=2;i<=n;i++) log[i]=log[i/2]+1;

模板:


#include
#include
#include
#define maxm 25
#define maxn 100005
using namespace std;
int n,m;
int a[maxn],Log[maxn];
int f[maxn][maxm];
int l,r,i,k,ans;
void GetLog(){
	int i;
	Log[1]=0;
	for(i=2;i<=n+1;++i)
	  Log[i]=Log[i/2]+1;
}
void RMQ(){
	int i,j;
	for(i=1;i<=n;++i)
	  f[i][0]=a[i];
	for(j=1;(1<<j)<=n;++j) //其实j<=21,j<=22也都行
	  for(i=1;i+(1<<j)-1<=n;++i)
	    f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int main(){
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	GetLog();
	RMQ();
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&l,&r);
		k=Log[r-l+1];
		ans=max(f[l][k],f[r-(1<<k)+1][k]);
		printf("%d\n",ans);
	}
	return 0;
}
 

你可能感兴趣的:(RMQ)