【概述】
RMQ : Range Maximum/minimum Query 这就是指区间最大或最小值(区间最值)
ST表:Spars Table,一种可以解决 RMQ 的,基于倍增的数据结构,利用 ST 算法预处理打出的表,称为 ST 表。
ST 算法:对于 RMQ 问题,给出 n 个数 m 次询问,每次询问区间最值,当 m 较小时,使用暴力即可解决,但随着 m 的增大,O(logn) 的的询问处理已经不够,需要 O(1) 的询问。而 ST 算法可以在 O(nlogn) 时间内进行预处理,然后在 O(1) 时间内回答每个查询,其实际上就是一种动态规划与打表的思想,缺点是不支持修改操作。
一、原理
要在 O(1) 求出区间的最值,一个很自然的想法是用动态规划处理的方法,用 dp[i][j] 来记录区间 [i,j] 的最大值,这样显然有状态转移方程:dp[i][j]=max(dp[i][j-1],a[j])。
dp[][] | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
1 | 5 | 5 | 7 | 7 | 12 | 12 | 12 | 12 | 12 | 15 |
2 | 3 | 7 | 7 | 12 | 12 | 12 | 12 | 12 | 15 | |
3 | 7 | 7 | 12 | 12 | 12 | 12 | 12 | 15 | ||
4 | 2 | 12 | 12 | 12 | 12 | 12 | 15 | |||
5 | 12 | 12 | 12 | 12 | 12 | 15 | ||||
6 | 1 | 6 | 6 | 8 | 15 | |||||
7 | 6 | 6 | 8 | 15 | ||||||
8 | 4 | 8 | 15 | |||||||
9 | 8 | 15 | ||||||||
10 | 15 |
但这样预处理是 O(n*n) 的,所以还要进一步的优化。
1、预处理(创建ST表)
max 函数满足一个性质:允许区间重叠,即 max(i,j)=max( max(i,k) , max(k,j) ),也就是说,可以由两个较小的有重叠的区间,直接推出一个大区间,从而减少维护的区间数量。
例如:有数组A 为:5 3 7 2 12 1 6 4 8 15 则下表为该数组的ST表
F[ ][ ] | 2^0=1 | 2^1=2 | 2^2=4 | 2^3=8 |
1 | 5 | 5 | 7 | 12 |
2 | 3 | 7 | 12 | 12 |
3 | 7 | 7 | 12 | 15 |
4 | 2 | 12 | 12 | |
5 | 12 | 12 | 12 | |
6 | 1 | 6 | 8 | |
7 | 6 | 6 | 15 | |
8 | 4 | 8 | ||
9 | 8 | 15 | ||
10 | 15 |
DP的状态:采用倍增的思想,假设是要求数组A[i]的区间最值,F[i,j] 表示从第 i 个数起连续 2^j 个数中的最大值。
F[1][0] 表示第 1 个数起,长度为 2^0=1 的最大值,其实就是 5。
F[1][1] = max(5,3) = 5 表示第 1 个数起,长度为 2^1=2 的最大值,其实就是 5。
F[1][2] = max(5,3,7,2) = 7 表示第 1 个数起,长度为 2^2=4 的最大值,其实就是 7。
F[1][3] = max(5,3,7,2,12,1,6,4)=12表示第 1 个数起,长度为 2^3=8 的最大值,其实就是 12。
F[2][2] = max(3,7,2,12)=12表示第 2 个数起,长度为 2^2=4 的最大值,其实就是 12。
DP的初始值: 可以看出 F[i,0] = A[i]
状态转移方程: 把 F[i][j] 平均分成两段 ( F[i][j] 一定是偶数个数字),从 i 到 i+2^(j-1)-1 为一段,i+2^(j-1) 到 i+2^j-1 为一段,长度均为 2^(j-1)。
于是得到F[i][j] = max( F[i] [ j-1] , F[i+2^(j-1) ] [j-1] )
F[3][2]=max(F[3][1],F[5][1])表示从第3个数起,长度为 2^2=4 的最大值,也就是长度为4的最大值,可以由前一列长度为2的两个最大值比较得出。
void ST_create()
{ for(int i = 1;i<=n;i++)F[i][0] = a[i];
int k=log2(n); //k=log(n)/log(2)
for(int j = 1;j<=k;j++)
{ for(int i = 1;i<=n-(1<<j)+1;i++)
st[i][j] = max(F[i][j-1],F[i+(1<<(j-1))][j-1]); //前一列是跨多少长度,第二个数的行坐标就跨多少行
}
}
2、查询
假设要查询的区间为 (L,R),那么需要找到覆盖这个闭区间 [L,R] 的最小幂。
由于区间长度为 R-L+1,因此可以取 s=log2(R-L+1),
则有:RMQ(L, R) = max{ F[L][s] , F[ R-2^s+1][s] }
例:要求区间 [1,5] 的最大值
则有:s= log2(5-1+1) = 2
则:RMQ(1,5) = max( F[1][2] , F[5-2^2+1][ 2]) = max(F[1][2] , F[2][2])
void ST_query(int l,int r)//求区间[l..r]的最值
{ int s=log2(r-l+1);
return max(F[l][s],F[r-(1<<s)+1)][s]); //取两个区间最值
}
模板题:洛谷P3865 【模板】ST 表