RMQ的ST算法(区间最值)

ST算法求解RMQ问题(区间最值)

 

效率:O(n log n)预处理,O(1)询问

思想:

用 f [ i ][ j ] 表示 以i 开头的区间,包括2^j 个元素的一段区间的最值

那么有初始化的初始化 f [ i ][ 0 ] = a[ i ] a[ I ]表示第I个元素的值)

 

然后就有两种初始化的方法

1) 选择一个位置为更新点,然后枚举 2 ^ j ,即固定I ,求 f [ const I ][ j ]

2) 每次选择一个区间长度,然后枚举位置,即固定 ,求 f [ I ][ const j ]

那么哪一种效率更高呢?

如果选择方案1,那么显然没有办法优化

那么只能是方案2了。

 

由于这都是求2 ^ j j为整数)的区间的长度,那么就可以直接由这个区间拆成的两个1 / 2长度的区间来求,令人欣慰的是,这一段区间在上一个步骤一定已经求过了。这样每一次计算都很容易,并且计算的次数是递减的。砖家证明,全部效率O(n log n)(因为初始化的初始化是在读数时就完成的,不计入)

下面贴出代码~~~

用的是二维vector~~~

 

# include <iostream>

# include <fstream>

# include <cstdio>

# include <cstdlib>

# include <cstring>

# include <cmath>

# include <algorithm>

# include <vector>

# include <deque>

# include <list>

# include <map>

 

 

using namespace std ;

 

int n , temp ;

vector <vector <int> > f ;

void init () ;

void st () ;

void ask () ;

int main ()

{

freopen ( "st.in"  , "r" , stdin  ) ;

freopen ( "st.out" , "w" , stdout ) ;

init () ;

st () ;

ask () ;

}

void init ()

{

vector <int> te ;

cin >> n ;

temp = log ( n ) / log ( 2 ) ;

for ( int i = 0 ; i <= n ; i ++ )

{

te.clear () ;

    for ( int j = 0 ; j <= temp ; j ++ )

        {

te.push_back ( 0 ) ;

        }

f.push_back ( te ) ;

}

for ( int i = 1 ; i <= n ; i ++ )

{

cin >> temp ;

f[ i ][ 0 ] = temp ;

}

cout << f.size () << '\n' << te.size () << '\n' ;

for ( int i = 0 ; i < f.size () ; i ++ )

    for ( int j = 0 ; j < te.size () ; j ++ )

    {

cout << i << ' ' << j << " : " << f[ i ][ j ] << '\n' ;

    }

}

void st ()

{

int kkk ;

for ( int j = 1 ; j <= temp ; j ++ )//1<<j+括号,位运算的级别貌似不怎么高

for ( int i = 1 ; i <= n - ( 1 << j ) + 1 ; i ++ ) //注意下面这里的是 i + 1 << ( j - 1 )最后没有-1

//因为比如求f[ 1 ][ 2 ]时,f[ 1 ][ 1 ] : 1 ~ 2 , 下一个应该是 f[ 3 ][ 1 ],正好+上一个一半的长度

        f[ i ][ j ] = max ( f[ i ][ j - 1 ] , f[ i + ( 1 << ( j - 1 ) ) ][ j - 1 ] ) ;

}

void ask ()

{

int q , l , r , maxn = 0 ;

cin >> q ;

for ( int i = 1 ; i <= q ; i ++ )

{

cin >> l >> r ;

temp = ( int )log ( r - l ) / log ( 2 ) ;//不要加 1,否则可能超出这个区间

maxn = max ( f[ l ][ temp ] , f[ r - ( 1 << temp ) + 1 ][ temp ] ) ;

cout << maxn << '\n' ;

}

}

 

 

调试这个代码费了我一下午!所以说一定要记住:位运算的左移(<<)和右移(>>)的级别特别低~~~!!!甚至低于+-!

posted on 2012-08-06 18:14  Stery  阅读(183)  评论(0编辑  收藏  举报

导航