zoukankan      html  css  js  c++  java
  • 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

    (完)

  • 相关阅读:
    Redis
    cut
    grep
    MySQL中EXPLAIN的解释
    MySQL数据类型
    有用的MySQL语句
    mysql函数
    memcache
    存储过程 游标的使用
    存储过程批量删除
  • 原文地址:https://www.cnblogs.com/shadowland/p/5870366.html
Copyright © 2011-2022 走看看