欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

RMQ(范围最小化问题)【倍增法】

程序员文章站 2024-03-22 13:51:34
...

问题描述

范围最小化问题(Range Minimum Query,RMQ)。给出一个 n 个元素的数组 A1,A2,,AnA_1, A_2,\dots,A_n,设计一个数据结构,支持查询 Query(L, R): 计算 minAL,AL+1,,ARmin{A_L, A_{L+1},\dots,A_R}

解题报告

dp[i][j]dp[i][j] 表示从 ii 开始的,长度为 2j2^j 的一段元素中的最小值,则可以用递推的方式计算 dp[i][j]dp[i][j]
dp[i][j]=min(dp[i][j1],dp[i+2j1][j1])dp[i][j]=min(dp[i][j-1], dp[i+2^{j-1}][j-1])
RMQ(范围最小化问题)【倍增法】
为什么 dp[i][j] 表示的是 从 ii 开始的,长度为 2j2^j 的一段元素中最小值呢?
如果这样的话,就需要在三个区间中找最小值,一方面状态转移方程不好写,另一方面,时间复杂度和空间复杂度并没有提升多少,所以是 2j2^j

注意到 2jn2^j\le n,因此 d 数组的元素个数不超过 nlognnlogn,而每一项都可以在常数时间计算完毕,故总时间为 O(nlong)O(nlong)

查询操作很简单,令 k 为满足 2kRL+12^k\le R-L+1 的最大整数,则以 LL 开头、以 RR 结尾的两个长度为 2k2^k 的区间合起来覆盖了查询区间 [L,R][L,R]。由于是取最小值,有些元素重复考虑了几遍也没有关系。

实现代码

  • 预处理
void RMQ_INIT(const vector<int>&A){
	int n= A.size();
	for(int i=0;i<n;i++)dp[i][0]=A[i];
	for(int j=1;(1<<j)<=n;j++){
		for(int i=0;i+(1<<j)-1<n;i++)
			dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1])
	}
}
  • 查询
int RMQ(int l, int r){
	int k=0;
	while((1<<(k+1))<=r-l+1) k++;
	return min(d[l][k], d[r-(1<<k)+1][k])
}

参考资料

[1] 算法竞赛·入门经典·训练指南 P197.

相关标签: 倍增法