淺談對ST表的一些理解


今天打了人生第一道ST表題(其實只是ST表跑得最快);

ST表是一種用來解決RMQ問題的利器。。。

大體操作有兩步:

第一部分nlogn預處理

第二部分O(1)詢問

 

預處理就是運用倍增+區間動規

ST表使用DP思想求解區間最值,貌似屬於區間動態規划,不過區間在增加時,每次並不是增加一個長度,而是使用倍增的思想,每次增加2^i個長度。

使用F[i,j]表示以i為起點,區間長度為2^j的區間最值,此時區間為[i,i + 2^j - 1]。

比如,F[0,2]表示區間[0,3]的最值,F[2,2]表示區間[2,5]的最值。

在求解F[i,j]時,ST算法是先對長度為2^j的區間[i,i + 2^j - 1]分成兩等份,每份長度均為2^(j - 1)。之后在分別求解這兩個區間的最值F[i,j - 1]和F[i + 2^(j - 1),j - 1]。,最后在結合這兩個區間的最值,求出整個區間的最值。

狀態轉移方程是 F[i,j] = min(F[i,j - 1],F[i + 2^(j - 1),j - 1])

初始狀態為:F[i,0] = A[i]。

代碼如下:

 1 void makeST()
 2 {
 3   pre[0]=1;for(int i=1;i<=20;i++) pre[i]=pre[i-1]<<1;
 4   pre2[0]=-1;for(int i=1;i<=n;i++) pre2[i]=pre2[i>>1]+1;
 5   for(int i=1;i<=n;i++) ST[i][0]=i;
 6   for(int j=1;j<=20;j++)
 7     for(int i=1;i<=n;i++){
 8       if(i+pre[j]-1<=n){
 9        int x1=ST[i][j-1],x2=ST[i+pre[j-1]][j-1];
10        if(a[x1]>a[x2]) ST[i][j]=x1;
11        else ST[i][j]=x2;
12       }
13     }
14 }

一開始打錯了好多個地方!!!醉了!!!

1.要把20的循環放在外面,手賤打錯了。。。

2.pre2數組是用來支持詢問的。。。相當於是logi,所以是for(1--n);

 

詢問操作:

在預處理期間,每一個狀態對應的區間長度都為2^i。由於給出的待查詢區間長度不一定恰好為2^i,因此我們應對待查詢的區間進行處理。

這里我們把待查詢的區間分成兩個小區間,這兩個小區間滿足兩個條件:(1)這兩個小區間要能覆蓋整個區間(2)為了利用預處理的結果,要求小區間長度相等且都為2^i。注意兩個小區間可能重疊。

在程序計算求解區間長度時,並沒有那么麻煩,我們可以直接得到i,即等於直接對區間長度取以2為底的對數。這里,對於區間[3,11],其分解的區間長度為int(log(11 - 3 + 1)) = 3,這里log是以2為底的。

根據上述思想,可以把待查詢區間[x,y]分成兩個小區間[x,x + 2^i - 1] 和 [y - 2^i + 1,y] ,其又分別對應着F[x,i]和F[y - 2^i + 1,i],此時為了求解整個區間的最小值,我們只需求這兩個值得最小值即可,此時復雜度是O(1)。

注意細節要加1。。。

代碼實現如下:

1 int query(int l,int r)
2 {
3   if(l==r)return l;
4   int x=pre2[r-l+1];
5   int x1=ST[l][x],x2=ST[r-pre[x]+1][x];
6   return a[x1]>a[x2]?x1:x2;
7 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM