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

问题描述

范围最小化问题(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])
在这里插入图片描述
为什么 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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章