1. 程式人生 > >算法詳解——st表

算法詳解——st表

tdi nlogn getchar 讓我 https 關於 color [] 用處

st表是解決區間RMQ(區間最值問題)的一類算法,時間復雜度為O(nlogn)的預處理和O(1)的查詢,其主要運用了類似倍增的思想...

總體來說,st表的用處還是挺大的,代碼也比較短,容易記...


st表

若現在給定一個長度為n的序列A,每次給定兩個數l,r,求出A[l]~A[r]中的最大值...

那麽我們考慮怎麽做,首先是樸素做法,每次詢問最大值時用for循環從l到r跑一遍,每次循環復雜度為O(r - l)

那有沒有一種算法可以讓我們以O(1)的復雜度就知道最大值呢?

這時我們引進st表算法...

具體做法:

我們定義一個二維數組st[i][j]表示區間[i,i+2^j-1]裏的最大值,那麽有st[i][0] = A[i],即A[i]是區間[i,i]的最大值,然後我們運用倍增的思想,每次維護區間最值就行了,預處理的代碼如下:

inline void init()
{
    for(int i = 1;i <= n;i ++) st[i][0] = a[i]; 
    for(int j = 1;j <= 21;j ++)
        for(int i = 1;(i + (1<<j) - 1) <= n;i ++)
            st[i][j] = max(st[i][j - 1],st[i + 1<<(j - 1)][j - 1]);
}

關於查詢,我們需要先對查詢區間的長度取一個對數k,那麽2^k一定大於等於區間長度的一半,接著我們從左右兩個端點處向內取最大值(即使有重疊也沒事),這樣我們就O(1)的查詢出了區間的最大值,代碼如下:

inline int query(int l,int r)
{
    int k = log(r - l + 1);
    return max(st[l][k],st[r - (1<<k) + 1][k]);
} 

下面有一道板子題

洛谷P3865

代碼如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

const int maxn = 1e5 + 5;
int n,m;
int st[maxn][23]; inline int read() { char ch = getchar();int x = 0,f = 1; while(ch<0||ch>9){if(ch == -) f = -1; ch = getchar();} while(ch>=0&&ch<=9){x = x*10 + ch -0; ch = getchar();} return x*f; } inline void init() { for(int i = 1;i <= 21;i ++) for(int j = 1;j+(1<<i)-1 <= n;j ++) st[j][i] = std::max(st[j][i-1],st[j+(1<<(i-1))][i-1]); } inline int query(int l,int r) { int k = log2(r-l+1); return std::max(st[l][k],st[r-(1<<len)+1][k]); } int main(int argc, char const *argv[]) { n = read(); m = read(); for(int i = 1;i <= n;i ++) st[i][0] = read(); init(); for(int i = 1;i <= m;i ++) { int l = read(); int r = read(); printf("%d\n",query(l,r)); } return 0; }

算法詳解——st表