RMQ问题:求给定区间极值问题,下面以最大值为例
ST(Sparse Table)算法基本也是核心的思想是基于区间划分的动态规划,即M[i,j] = max{ M[i,r-1] , M[r,j] }。M[i,j] 表示区间[i,j]上的最大值,r位于[i,j]中间。
故而不知那位大神开发出如下ST算法:
状态转移方程:M[ i , j ] = max{ M[ i , j-1 ] , M[ i+(1<<j-1) , j-1] } 。M[ i , j ]表示从下标i开始长度2^j的区间。
这样,在预处理是只需要按递推的方式计算每个M[ i , j ],约束条件1<<j<=N(区间长度),i+(1<<j)-1<N(区间数目)。M[ i ,0 ]初始化为区间第i个元素自身。这样,预处理时间复杂度仅为O(NlgN),空间复杂度也为NlgN。
当查找任意区间[ x , y ]的极值时,只需要先求出最大的k使得1<<k<=(y-x+1),即求小于给定区间长度的最长的2^k型区间长度(因为预处理产生的极值区间均为2的幂次长度)。然后,[ x , y ]上的极值必然是[ x , x+(1<<k)-1 ]或[ y-(1<<k)+1 , y ]两者中的极值。对于任意查询Query,即有:
Query[x,y]=max{ M[ x , k ] , M[ y-(1<<k)+1 , k ] },O(1)复杂度时间内就可以存预处理数组中查找计算出任意区间的极值。
因为大多数题目若是涉及到RMQ便是需要多次来SEARCH,所以ST算法在占有一定空间的情况
下能获得较好的时间复杂度。
下面是我写的预处理和查询的代码:
10 void Preproccess(void) //预处理,array为原数组,max_index存放极值元素下标
11 {
12 for(i=0;i<N;i++) max_index[i][0]=i;
13 for(j=1;(1<<j)<=N;j++)
14 {
15 for(i=0;i+(1<<j)-1<N;i++)
16 {
17 if(array[max_index[i][j-1]]>array[max_index[i+(1<<(j-1))][j-1]])
18 max_index[i][j]=max_index[i][j-1];
19 else max_index[i][j]=max_index[i+(1<<(j-1))][j-1];
20 }
21 }
22 }
23 int Query(int x,int y) //查询区间[ x , y ]极值
24 {
25 int k,l=y-x+1;
26 for(k=0;(1<<k)<=l;k++);k--;
27 if(array[max_index[x][k]]>array[max_index[y-(1<<k)+1][k]])
28 return array[max_index[x][k]];
29 else return array[max_index[y-(1<<k)+1][k]];
30 }