RMQ问题中的ST算法

RMQ问题中的ST算法

RMQ(Range Minimum Query,区间最小值查询)问题是计算机科学中一个经典的问题,它在很多实际应用中都有广泛的应用,如图像处理、信号处理等。稀疏表算法是解决RMQ问题的一种高效方法。本文将详细介绍稀疏表算法的原理及算法流程,并给出相关的C++代码实现。

原理

稀疏表算法的基本思想是将原始数据集构建成一个稀疏矩阵,然后通过查询稀疏矩阵来得到区间最小值。稀疏矩阵中的非零元素表示原始数据集中的最小值,而零元素则表示原始数据集中没有最小值的位置。通过这种方式,我们可以在O(1)的时间复杂度内完成区间最小值的查询。

算法流程

稀疏表算法的流程如下:

  1. 初始化:创建一个大小为n×m的二维数组S,其中n表示原始数据集的大小,m表示查询区间的大小。将S的所有元素初始化为0。

  2. 构建稀疏矩阵:遍历原始数据集,对于每个数据点d[i],如果其在查询区间[l, r]内,则更新S[i][j] = min(S[i][j], d[i]),其中j表示当前查询区间的下标。

  3. 查询区间最小值:给定一个查询区间[l, r],遍历稀疏矩阵S,找到第一个大于等于0的元素S[i][j],此时S[i][j]即为区间[l, r]的最小值。

C++代码实现

下面给出稀疏表算法的C++代码实现:

// RMQ问题中的ST算法
// m次询问,求l~r中最大值是多少
#include<bits/stdc++.h>
#define reg register
using namespace std;

// 读取输入的函数
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

// 输出结果的函数
inline void write(int x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9) write(x/10);
	putchar(x%10+'0');
	return ;
}

const int MAXN=100005;
vector<int > a; // 存储输入数据的数组
int n,f[MAXN][21],m; // f[i][j]表示区间[i, j]的最大值

// ST预处理函数
void ST_prework(){
	for(reg int i=1;i<=n;i++) f[i][0]=a[i]; // 将第一个元素作为区间的最大值
	int t=log(n)/log(2)+1; // 确定二维数组f的维度
	for(reg int j=1;j<t;j++){ // 从第二个维度开始遍历二维数组f
		for(reg int i=1;i<=n-(1<<j)+1;i++){ // 从第一个维度开始遍历一维数组f的第一部分
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); // 根据状态转移方程更新f[i][j]的值
		}
	}
	return ;
}

// ST查询函数,返回区间[l, r]的最大值
int ST_query(int l, int r){
	int g=log(r-l+1)/log(2); // 计算区间长度的对数,向上取整得到二进制位数g
	return max(f[l][g],f[r-(1<<g)+1][g]); // 根据状态转移方程和边界条件返回区间最大值
}

// 主函数,读取输入数据,调用ST预处理函数和查询函数,输出结果
int main(){
	n=read(),m=read(); // 读取输入数据的数量n和查询次数m
	a.push_back(0); // 在a数组末尾添加一个元素,用于记录空元素的位置,方便后续处理边界情况
	for(reg int i=1;i<=n;i++)a.push_back(read()); // 按照顺序读取输入数据并存储到a数组中
	ST_prework(); // 对输入数据进行预处理,构建ST表f
	for(reg int i=1;i<=m;i++){ // 对于每次查询,调用ST查询函数并输出结果
		int p=read(),q=read(); // 读取查询的起始位置p和结束位置q
		write(ST_query(p, q)); // 将查询结果写入输出流中,并换行分隔每个查询结果
		putchar('\n'); // 在每次查询后换行,使得输出更加清晰易读
	}
	return 0;
}

例题及题解

例题:给定以下数据集和查询区间:

5 4
1000000000 999999998 999999997 999999996 999999995
1000000001 1000000002 1000000003 1000000004 1000000005
posted @ 2023-08-07 21:59  Nebulary  阅读(20)  评论(0编辑  收藏  举报