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

演算法詳解——st表

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