问题描述
范围最小化问题(Range Minimum Query,RMQ)。给出一个 n
个元素的数组
,设计一个数据结构,支持查询 Query(L, R):
计算
。
解题报告
令
表示从
开始的,长度为
的一段元素中的最小值,则可以用递推的方式计算
。
为什么 dp[i][j]
表示的是 从
开始的,长度为
的一段元素中最小值呢?
如果这样的话,就需要在三个区间中找最小值,一方面状态转移方程不好写,另一方面,时间复杂度和空间复杂度并没有提升多少,所以是
。
注意到
,因此 d
数组的元素个数不超过
,而每一项都可以在常数时间计算完毕,故总时间为
查询操作很简单,令 k
为满足
的最大整数,则以
开头、以
结尾的两个长度为
的区间合起来覆盖了查询区间
。由于是取最小值,有些元素重复考虑了几遍也没有关系。
实现代码
- 预处理
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.