RMQ问题之ST算法

RMQ问题之ST算法

RMQ(Range Minimum/Maximum Query)问题,即区间最值问题。
给你n个数,a1 , a2 , a3 , ... ,an,求出区间 [ l , r ]的最大值。


举例:
a={ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 },求出区间[4 ,8]中的最值。(答案:8 )

这个问题最朴素的想法是用一个循环每次比较大小,但是,当数据范围较大时,这个算法十分低效。这时我们往往使用 ST 算法解决这个问题。虽然线段树和树状数组都能解决,但是ST算法更快。ST算法能做到O(1)时间的查询,而且代码实现更容易。

我们定义 f ( i , j ) 表示从i开始,长度为 2j 的一段区间中的最大值。

例如:在数列3,2,4,5,6,8,1,2,9,7中,f ( 1 , 0 )表示从第一个数开始长度为20的区间内的最大值,即f ( 1 , 0 ) = 3 , 同理f ( 1 , 1 )=3 , f ( 1 , 2 ) =5, f ( 1 , 3 ) =8。从这里很容易发现,f ( i , 0 ) 等于原数列第i个数的值。

可以通过预处理的递推计算f ( i , j ):


f ( i , j ) = max { f ( i , j-1 )  , f ( i+(1<<j-1) , j - 1 )  }


这个方程与动态规划的思想十分相似,这几乎是ST算法的核心,但是,这个方程是什么意思呢?我们将区间[ i , j ]分成两部分[ i , i+2j-1 -1 ] 与 [ i+2j-1 , i+2j -1] , 这两个区间的长度都为2j-1,分别求出两个区间最大值,在取较大的那个,就是原区间的最大值。这就是ST算法的动态转移方程。

举例:数列a={ 1 ,4 , 2 , 3 }求f ( 1 , 2 ) =max { f( 1 , 1 ) , f ( 3 , 1 ) }=max { 4 , 3 } = 4 ;
注:初始状态 f ( i , 0 ) = a [i] ;


预处理:

 1 void Init()//nlogn
 2 {
 3     log2[1] = 0;
 4     for(int i = 2; i <= n; i++) log2[i] = log2[i >> 1] + 1; //打log2表
 5     for(int i = 1; i <= n; i++) f[i][0] = a[i]; //建立初始状态
 6     for(int j = 1; (1 << j) <= n; j++)
 7     {
 8         for(int i = 1; i + (1 << j) - 1 <= n; i++)
 9         {
10             f[i][j] = max( f[i][j - 1] , f[i + (1 << j - 1)][j - 1] ); //动态转移方程
11         }
12     }
13 }

查询:


查询区间[a , b ]中最大值,查询的方法比较简单,我们只需要找到一个最大整数 k ,使它满足2k<= b - a +1,例如[ 3 , 11 ] 可以分为 [ 3 , 9 ]
这里我们把待查询的区间分成两个小区间,这两个小区间满足两个条件:(1)这两个小区间要能覆盖整个区间(2)为了利用预处理的结果,要求小区间长度相等。注意两个小区间可能重叠(区间重叠不影响结果)
直接返回 max{ f[a][k] , f[b-(1<<k)+1][k] },于是就求出查询区间中的最大值。

代码如下:

1 int Query(int a, int b)
2 {
3     int k = log2[b - a + 1];
4     return max( f[a][k] , f[b - (1 << k) + 1][k] );
5 }


主程序:

 1 int main()
 2 {
 3     int m, u, v;
 4     cin >> n;
 5     for(int i = 1; i <= n; i++)
 6     {
 7         cin >> arr[i];
 8     }
 9     Init();
10     cin >> m;
11     while(m --)
12     {
13         cin >> u >> v;
14         if(u > v) swap(u, v);
15         cout << Query(u, v) << endl;
16     }
17     return 0;
18 }


综上,ST算法在只有查询的情况下,十分高效,在做了O(nlogn)的预处理后,可以做到O(1)的时间查询。

2016-09-14

(完)

原文地址:https://www.cnblogs.com/shadowland/p/5870366.html