1. 程式人生 > >ST(Sparse Table)演算法基本思路及實現

ST(Sparse Table)演算法基本思路及實現

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;
}