RMQ-ST:多次查找最大最小值

参考资料:RMQ问题ST算法

题目来源:#1068 : RMQ-ST算法

一、问题描述:

Range Maximum/Minimum Query(RMQ) 即,给定一整数序列x1,x2,…,xn,编号为1, 2, ..., n,对给定编号区间[L, R],求该区间对应的整数序列中的最大或最小值。

输入:每个测试点(输入文件)有且仅有一组测试数据。每组测试数据的第1行为一个整数N,意义如前文所述。每组测试数据的第2行为N个整数,分别描述每种商品的重量,其中第i个整数表示标号为i的商品的重量weight_i。每组测试数据的第3行为一个整数Q,表示小Hi总共询问的次数。每组测试数据的第N+4~N+Q+3行,每行分别描述一个询问,其中第N+i+3行为两个整数Li, Ri,表示小Hi询问的一个区间[Li, Ri]。对于100%的数据,满足N<=10^6,Q<=10^6, 1<=Li<=Ri<=N,0<weight_i<=10^4。

输出:对于每组测试数据,对于每个小Hi的询问,按照在输入中出现的顺序,各输出一行,表示查询的结果:标号在区间[Li, Ri]中的所有商品中重量最轻的商品的重量。

样例输入:

10
7334
1556
8286
1640
2699
4807
8068
981
4120
2179
5
3 4
2 8
2 4
6 8
7 10
样例输出:

1640
981
1556
981
981

二、问题分析:

最直接的方法是对编号区间[L, R]对应的整数进行遍历,求得最大最小值,此时时间复杂度为O(N),但当查询次数m很大时,时间复杂度为O(nm)。采用 Sparse Table(ST) 算法,可以将查询复杂度降维O(1),而创建ST的预处理操作复杂度仅为O(nlogn)。对最小值问题做如下讨论(对最大值问题只需稍加修改)。

1. 预处理(构建ST):

设f(i, j)表示编号区间[j, j+2^i-1],对应整数序列中的最小值。令整数k满足2^k<=n<2^(k+1),则有:

f(0, j)=xj, 1<=j<=n;

f(i, j)=min{f(i-1, j), f(i-1, j+2^(i-1))}, 1<=i<=k, 0<=j<=n-2^i;

2. 查询:

对编号区间[L, R],设2^i<=(R-L+1)<2^(i+1),则该区间可分解为两个区间[L, 2^i]和[R-2^i+1, R]。故对应整数序列中的最小值为min{f(i, L), f(i, R-2^i+1)}。其中f(i, L)表示编号区间[L, 2^i]对应整数序列中的最小值, f(R-2^i+1, R)表示编号区间[R-2^i+1, R]对应整数序列中的最小值。

三、代码实现:

1. 代码实现1:

#include<cstdio>
#include<cstring>
#define get_m(x,y) ((x)<(y)?(x):(y))

int a[21][1000001];
int main(int argc,char *argv[]){
	int n,m,x,y;
	scanf("%d",&n);
	for(int j=0;j<n;++j){
		scanf("%d",*a+j);
	}
	for(int i=1,t=1;t<n;t<<=1,++i){
		for(int j=0;j+(t<<1)<=n;++j){
			a[i][j]=get_m(a[i-1][j],a[i-1][j+t]);
		}
	}

	scanf("%d",&m);
	while(m--){
		scanf("%d%d",&x,&y);
		int len=y-x+1;
		int i=0,t=1;
		while(len!=1){
			t<<=1;
			len>>=1;
			++i;
		}
		printf("%d\n",get_m(a[i][x-1],a[i][y-t]));
	}
	return 0;
} 

若要求最大值,只需将"#define get_m(x,y) ((x)<(y)?(x):(y))"替换为"#define get_m(x,y) ((x)>(y)?(x):(y))"。

2. 代码实现2:

#include<cstdio>
#include<cstring>
#include<cstdlib>

class RMQ_ST{
public:
	RMQ_ST(int *a,int n,bool f);
	int query(int x,int y);
	~RMQ_ST();
private:
	static int get_k(int n);
	void build_st(int *a,int n);
	int min(int x,int y){
		return x<y?x:y;
	}
	int max(int x,int y){
		return x>y?x:y;
	}
	int size;
	int size_k;
	int **st;
	int (RMQ_ST::*get_m)(int x,int y);
};
RMQ_ST::RMQ_ST(int *a,int n,bool flag_m){
	if(a==NULL||n<1){
		exit(0);
	}
	size=n;
	size_k=get_k(n);
	if(flag_m){
		get_m=&RMQ_ST::min;
	}else{
		get_m=&RMQ_ST::max;
	}
	if((st=new int*[size_k])==NULL){
		exit(0);
	}
	for(int i=0;i<size_k;++i){
		if((st[i]=new int[size])==NULL){
			exit(0);
		}
	}
	build_st(a,size);
}
RMQ_ST::~RMQ_ST(){
	if(st!=NULL){
		for(int i=0;i<size_k;++i){
			if(st[i]){
				delete[] st[i];
				st[i]=NULL;
			}
		}
		delete[] st;
		st=NULL;
	}
}
int RMQ_ST::query(int x,int y){
	if(x<1||y>size||x>y){
		exit(0);
	}
	int k,t=1;
	k=get_k(y-x+1)-1;
	t<<=k;
	return (this->*get_m)(st[k][x-1],st[k][y-t]);
}
void RMQ_ST::build_st(int *a,int n){
	for(int j=0;j<n;++j){
		st[0][j]=a[j];
	}
	for(int i=1,t=1;t<n;t<<=1,++i){
		for(int j=0;j+(t<<1)<=n;++j){
			st[i][j]=(this->*get_m)(st[i-1][j],st[i-1][j+t]);
		}
	}
}
int RMQ_ST::get_k(int n){
	int i=0;
	while(n!=0){
		n>>=1;
		++i;
	}
	return i;
}

int main(int argc,char *argv[]){
	int n,m,x,y;
	int *a=NULL;

	scanf("%d",&n);
	a=new int[n];
	for(int j=0;j<n;++j){
		scanf("%d",a+j);
	}
	RMQ_ST rms_st(a,n,true);
	
	scanf("%d",&m);
	while(m--){
		scanf("%d%d",&x,&y);	
		printf("%d\n",rms_st.query(x,y));
	}
	return 0;
}
若求最大值,只需将"RMQ_ST rms_st(a,n,true);"替换为"RMQ_ST rms_st(a,n,false);"。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值