RMQ问题(Range Minimum Query):
概念
对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
求解方法:
朴素方法:
我们预处理阶段时间复杂度为:O(n)
我们查询阶段时间复杂度为:O(n)
线段树:
这是一个比较简单的线段树问题,咱们求解一段区间的最值,需要用到懒操作
详情请看:线段树
ST(Sparse Table)算法
- 建立一个二维数组F[1…N][1…O(log2N)]的整数值
在这个数组中F[i][j]表示范围[i,i+2^j -1]中的最大值
(例如[2,5]中的最大值被计为F[2][2])。
建立数组的过程可以在O(NlogN)时间内完成。
使用动态规划的思想
void Initialise(vector<int> &Array)
{
int Length = (int)Array.size();
// 长度为 0 时,表示数据自身。
for (int i = 0; i < Length; ++i) F[i][0] = Array[i];
for (int j = 1; (1 << j) <= Length; ++j)
for (int i = 0; i + (1 << j) - 1 < Length; ++i)
F[i][j] = min(
F[i][j - 1], F[i + (1 << (j - 1))][j - 1]
); // 分成长度相等的两段
}
- 查询
当我们需要计算一个区间中的最大值(例如 [3,8]时),我们先求出这个区间的长度(即 8 - 3 + 1 = 6)。
然后我们算出不大于它的最大2^k值的指数(即 2)。我们要查询分别以3开头,和以 8 结尾的两个长度为2^2的区间的最大值。
显然F[3][2]和 F[5][2]也就是([3,6]和[5,8]的最大值就是[3,8]的最大值。查询的时间是常数,代码如下:
int Query(int Left, int Right)
{
int i = 0;
while (1 << (i + 1) <= Right - Left + 1) ++i;
return min(
F[Left][i], F[Right - (1 << i) + 1][i]
);
}
感谢:
1. ST算法:https://zh.wikipedia.org/wiki/%E8%8C%83%E5%9B%B4%E6%9C%80%E5%80%BC%E6%9F%A5%E8%AF%A2
2. RMQ问题简介:
http://kmplayer.iteye.com/blog/575725