初识RMQ大法----查询区间最值

RMQ问题为求区间最值的问题

线段树可以在O(logN)的时间复杂度内完成询问操作。

但是ST算法可以在常数时间内完成询问操作

ST算法:基于动态规划求区间最值的算法。

分为预处理和查询两部分

预处理:定义 F[i][j] 为从 i开始到 i+2^j-1 区间内的最值 , 我们可以讲这段2^j的区间分成两部分长度都为2^(j-1)的相同区间

区间1 为  i.....i+2^(j-1)-1   区间2为  i+2^(j-1).....i+2^j-1

那么可以得到  F[i][j] =Max( F[i][j-1],F[i+2^(j-1)][j-1],边界条件为F[i][0]=A[i].

由于大的区间是由小的区间得到的,所以预处理时必须按区间长度递增的顺序递推出F[i][j].

查询:求区间[ i , j ]的最值   令 d=(int) log2( j-i+1)   

我们取靠i的长度为2^d区间 以及靠j的2^d区间内的最大值 ,两个区间内可以存在公共部分

 则i,j max= Max ( F[i][d] ,F[j-2^d+1,d])

 给定一个数列,输出访问区间的最大值和最小值的差

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
ll a[50001];
ll maxn[50001][20];
ll minn[50001][20];
ll n;
//dp+二分 
//预处理 RMQ
void init(){
	for(int i=1;i<=n;i++){
		maxn[i][0]=minn[i][0]=a[i];
	}
	for(int j=1;(1<<j)<=n;j++){//枚举区间长度 
		for(int i=1;i+(1<<j)-1<=n;i++){//枚举区间左端点 
		//max(区间[i,i+(1<<j)-1]的左半区间[i,i+(1<<(j-1))-1]即maxn[i][j-1],区间[i,i+(1<<j)-1]的左半区间[i+(1<<(j-1)),i+(1<<j)-1],即maxn[i+(1<<(j-1))][j-1])
			maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
			minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
		}
	}
}
ll solve(ll x,ll y){
	ll k=(ll)(log(y-x+1)/log(2));
	//以上k的求法保证 maxn[x][k]和maxn[y-(1<<k)+1][k]有交集,同理。。。。 
	return max(maxn[x][k],maxn[y-(1<<k)+1][k])-min(minn[x][k],minn[y-(1<<k)+1][k]);	
}

int main(){
	ll q,x,y;
	scanf("%lld %lld",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%lld",a+i);
	}
	init();//预处理 
	while(q--){
		scanf("%lld %lld",&x,&y);
		printf("%lld\n",solve(x,y));
	}
	return 0;
}
发布了186 篇原创文章 · 获赞 38 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43746332/article/details/103396572
今日推荐