RMQ问题
RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
主要方法及复杂度如下:
1、朴素(即搜索),O(n)-O(qn) online。
2、线段树,O(n)-O(qlogn) online。
3、ST(实质是动态规划),O(nlogn)-O(q) online。
ST算法(Sparse Table),以求最大值为例,设d[i,j]表示[i,i+2^j-1]这个区间内的最大值,那么在询问到[a,b]区间的最大值时答案就是max(d[a,k], d[b-2^k+1,k]),其中k是满足2^k<=b-a+1(即长度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
d的求法可以用动态规划,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。
4、RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(q) online。
首先根据原数列,建立笛卡尔树,从而将问题在线性时间内规约为LCA问题。LCA问题可以在线性时间内规约为约束RMQ,也就是数列中任意两个相邻的数的差都是+1或-1的RMQ问题。约束RMQ有O(n)-O(1)的在线解法,故整个算法的时间复杂度为O(n)-O(1)。 - 度娘
ST算法
适用范围
今天学习的是RMQ的ST算法,先说明一下它的适用范围:
复杂度是O(nlogn)的建表和O(1)的查询,并且不适用于更新。
如果更新的话就用线段树了,因为ST是直接建表了。
ps.(1 << k ) = 2^k
初始化
以最大值为例,最小值的初始化过程相同。
首先设A是要求区间最值的数列,f[i, j],表示从第i个数起连续 ( 1 << j )个数起连续数中的最大值。
比如来个数列:
1 2 3 5 7 9 2 10 3 4
f[1, 0]表示从第1个数起,长度为(1<<0) = 1位数范围的最大值,为1;
f[1, 2]表示从第1个数起,长度为(1<<2) = 4位数范围的最大值,为5;
f[2, 0]…为2;
f[2, 2]…为3;
可以发现,f[i, 0] = A[i]。
接下来是状态转移方程:
将f[i, j]分成两段,从 i 到 i+(1<<(j - 1))-1为一段,i + (1<<(j-1))到 i + (1 << j) - 1为一段,求解这段的最大值就行了。
状态转移方程:
f[ i, j ] = max( f[ i ][ j - 1 ] , f[ i + (1<<(j - 1)) ] [j - 1] )
状态转移方程表示的意义:
数列A中,以第i个数开头,总共(1 << j)位的连续子集的最大值,为以i开头共(1<<(j-1))位的连续子集 和 以i+(1<<(j-1))开头共(1<<(j-1))位的连续子集中的最大值。
初始化代码:
void queryInit()
{
////////////////////////////
for (int i = 1; i <= n; i++)
{
minPoint[i][0] = maxPoint[i][0] = ///;
}
////////////////////////////
for (int j = 1; (1 << j) <= n; j++)
{
for (int i = 1; i + (1 << j) - 1 <= n; i++)
{
int p = (1 << (j - 1));
minPoint[i][j] = Min(minPoint[i][j - 1], minPoint[i + p][j - 1