RMQ問題(ST算法)

RMQ是詢問某個區間內的最大值或最小值的問題,ST算法能夠求解RMQ問題.ST算法一般用在要 屢次詢問某一些區間的問題中,相比於線段樹,它的程序實現更加簡單,運行速度更快,它能夠作到O(nlogn)的預處理,O(1)回答每一個問題.使用ST算法的條件是沒有修改操做,所以它適用於沒有修改操做而且訪問次數較多(10^6級別甚至更大)的狀況.ios

1.預處理算法

ST算法的原理其實是動態規劃,首先要知道f數組的含義,f[i][j]中i表示左邊端點,j表示2^j個長度, 所以f[i,j]表示的是區間爲[i,i+2^j-1]這個範圍內的最大值,也就是以a[i]爲起點連續的2^j個數的最大值.因爲元素個數爲2^j個,因此從中間平均分紅兩部分,每一部分的個數都爲2^(j-1);假設f[6][3]分爲f[6][2]和f[10][2],以下圖所示,
RMQ問題(ST算法)
整個區間的最大值必定是左右兩部分最大值的較大者,知足動態規劃的最優化原理,分析得f數組的狀態轉移方程爲f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1]).數組

for(int j = 1;(1<<j) <= n;++j) //j枚舉每個可能出現的長度 
    for(int i = 1;i + (1<<j)-1 <= n;i++)  //i枚舉每個區間的左端點 
    f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);

2.詢問

當要訪問區間[L,R]的最大值,須要知道區間的長度len,mn數組存放的是小於等於必定長度len的最大的2的冪次,若要訪問區間[5,10]的最大值,則要先計算出men[len]的值,那麼區間[5,10]=[5,8]U[7,10].
RMQ問題(ST算法)ide

代碼實現優化

//對於必定長度的區間len,mn[len]表示小於等於len的最大的2的冪次 
   for(int len = 1;len <= n;++len)
   {
     int k = 0;
     while(1<<(k+1) <= len)
     k++;
     mn[len] = k;
   }

3.求區間[x,y]最大值spa

int k = mn[R - L + 1];
ans = max(f[L][k],f[R-(1<<k)+1][k]);

代碼實現code


#include<iostream>
using namespace std;
const int maxn=1e6+5; 
int f[maxn][25];
int mn[maxn];
int a[maxn];
int m,n;
void rmq_init()
{
  //初始化全部長度爲1的區間的最大值 
  for(int i = 1;i <= n;i++)    
  f[i][0] = a[i];

  for(int j = 1;(1<<j) <= n;++j) //j枚舉每個可能出現的長度 
    for(int i = 1;i + (1<<j)-1 <= n;i++)  //i枚舉每個區間的左端點 
    f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);

  //對於必定長度的區間len,mn[len]表示小於等於len的最大的2的冪次 
   for(int len = 1;len <= n;++len)
   {
     int k = 0;
     while(1<<(k+1) <= len)
     k++;
     mn[len] = k;
   }
}
 int rmq(int L,int R)
{
   int s = mn[R-L+1];
  int ans = max(f[L][s],f[R - (1<<s) + 1][s]);
  return ans;
 }
int main()
{
  cin>>n;
  for(int i = 1;i <= n;i++)
  cin>>a[i];
  rmq_init();
  int L,R;
  cin>>m;
  for(int i = 1;i <= m;i++)
  {
    cin>>L>>R;
    cout<<rmq(L,R)<<endl;
  }
  return 0;
 }

結果截圖
RMQ問題(ST算法)blog

相關文章
相關標籤/搜索