ST(Sparse Table)演算法基本思路及實現
阿新 • • 發佈:2018-12-20
ST演算法是在倍增的思想上建立的
來看一下ST演算法是怎麼實現的(以最大值為例):
首先是預處理,用一個DP解決。設a[i]是要求區間最值的數列,f[i,j]表示從第i個數起連續2^j個數中的最大值。
例如數列3 2 4 5 6 8 1 2 9 7
f[1,0]表示第1個數起,長度為2^0的最大值,其實就是3。
f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……從這裡可以看出f[i,0]其實就等於a[i]。
這樣,Dp的狀態、初值都已經有了,剩下的就是狀態轉移方程。
f[i j]=max(f[i][j-1],f[i+(1<<j)][j-1])
什麼意思呢?
我們把f[i,j]的求值區間平均分成兩段(因為2^j一定是偶數):
從i到i+2^(j-1)-1為一段,i+2^(j-1)為到i+2^(j-1)-1為為一段(長度都為2^(j-1)))。用上例說明,當i=1,j=3時就是3,2,4,5 和 6,8,1,2這兩段。f[i,j]就是這兩段的最大值中的最大值。
F[i,j]表稱為稀疏表(Sparse Table)。有了稀疏表,我們就可以做到O(1)時間查詢任意區間的最值。
如在上例中我們要求區間[2,8]的最大值,就要把它分成[2,5]和[5,8]兩個區間,因為這兩個區間長度為4,最大值我們可以直接由f[2,2]和f[5,2]得到。擴充套件到一般情況,就是把區間[l,r]分成兩個長度為2^j的區間(保證有f[i,j]對應,其中2^j為小於r-l+1的最大值),例如我們要查詢區間[2,6],則j=2,我們查詢的是兩個區間[2,5],[3,6]。
我們用q[l,r] 表示 從第l個數到第r個數中最大的數,則可以得到:
q[l,r]=max(f[l,j],f[r-(2^j)+1,j]);(j=log(j-r+1)/log(2));
下面是程式碼模板:
int n,q; //n個數,q個查詢 int a[50001]; //待查詢資料 int f[50001][20]; int max(int a,int b) { return a>b?a:b; } void rmq_init() { int i,j,k,m; m=(int)(log(n)/log(2.0));//保險起見用2.0 //求m,使得2的m次方不大於n for(i=1;i<=n;i++) { f[i][0]=a[i]; } for(j=1;j<=m;j++) { for(i=1;i<=n;i++) { f[i][j]=f[i][j-1]; k=1<<(j-1);//k等於2的(j-1)次方 if(i+k<=n) //注意邊界 f[i][j]=max(f[i][j],f[i+k][j-1]); } } } int rmq_query(int l,int r) { int i,m; m=(int)(log(r-l+1)/log(2.0)); i=max(f[l][m],f[r-(1<<m)+1][m]); return i; }