RMQ問題之ST算法
阿新 • • 發佈:2017-10-19
splay 我們 tdi scan 查詢 優化 col 2.0 ==
RMQ問題:求長度為n的數列中,求[i,j]直接的最值。
ST算法:一種動態規劃的方法。
一、預處理dp數組
對於區間[i,i+2^j-1]的最值,只需要知道區間[i,i+2^(j-1)-1]和區間[i+2^(j-1),i+2^j-1]的最值即可。
由此可的遞推方程:dp[i,i+2^j-1] = max(dp[i,i+2^(j-1)-1],dp[i+2^(j-1),i+2^j-1])
但是對於一個比較長的數列,2^j是一個非常大的數,我們也可發現,沒什麽必要直接記錄左右端點。
優化為i記錄一個起點,j記錄類似距離的東西,dp[i,j]表示區間[i,i+2^j-1]。
優化後遞推方程:dp[i,j] = max(dp[i,j-1],dp[i+2^(j-1),j-1])
預處理dp數組時間復雜度為O(nlogn)。
二、查詢最值
所以開始把一個區間當dp數組求出來,再進行查詢即可。
但是查詢時候,知道的是兩個端點l,r。
對於區間[l,r],如何再dp數組中查詢呢?
前面講了i表示起點,j代表類似距離的東西。
很明顯l就是i了。可是2^j-1不可能是r,這時候就要找個中間的"j"了。
令len = r-l+1, 則2^k <= len(此處註意不是2^k-1,反證:當2^k-1 == len時,l + 2^k-1 = l + len = r + 1 > r)
即[l,r] = max(dp[l,k],dp[r-(2^k)+1,k])
查詢時間復雜度為O(1)。
盡管代碼比較簡潔並且功能比較強大,速度也比較快,可是並沒有線段樹那麽功能多。
1 #include <cstdio> 2 #include <cmath> 3 #include <iostream> 4 using namespace std; 5 const int maxn = 100005; 6 int ma[maxn][20]; 7 int mi[maxn][20]; 8 int a[maxn]; 9 void init(int n){ 10 for(int i = 1; i <= n; ++i) 11 ma[i][0ST算法] = mi[i][0] = a[i]; 12 for(int j = 1; (1<<j) <= n; ++j) 13 for(int i = 1; i+(1<<j)-1 <= n; ++i){ 14 ma[i][j] = max(ma[i][j-1], ma[i+(1<<(j-1))][j-1]); 15 mi[i][j] = min(mi[i][j-1], mi[i+(1<<(j-1))][j-1]); 16 } 17 } 18 int rmq_max(int l, int r, int k){ 19 return max(ma[l][k], ma[r-(1<<(k))+1][k]); 20 } 21 int rmq_min(int l, int r, int k){ 22 return min(mi[l][k], mi[r-(1<<(k))+1][k]); 23 } 24 int rmq(int l, int r){ 25 int k = 0; 26 while(1<<(k+1) <= r-l+1) 27 ++k; 28 //int k = (int)(log(1.0*(r-l+1))/log(2.0));//也可以直接算出k 29 //算出k後,具體情況進行調用函數計算。例如返回最大差值. 30 return rmq_max(l,r,k) - rmq_min(l,r,k); 31 } 32 int main(){ 33 int n,q; 34 scanf("%d%d",&n,&q);//n個數,q次查詢次數 35 for(int i = 1; i <= n; ++i)//輸入n個數 36 scanf("%d",a+i); 37 init(n); int l,r; 38 while(q--){//進行q次查詢 39 scanf("%d%d",&l,&r); 40 printf("%d\n",rmq(l,r)); 41 } 42 return 0; 43 }
RMQ問題之ST算法