演算法詳解——st表
阿新 • • 發佈:2018-11-24
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]); }
下面有一道板子題
程式碼如下:
#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; }