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) 每次选择一个区间长度,然后枚举位置,即固定 j ,求 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' ;
}
}
调试这个代码费了我一下午!所以说一定要记住:位运算的左移(<<)和右移(>>)的级别特别低~!~~!!!甚至低于+-!