1. 程式人生 > >RMQ算法

RMQ算法

代碼實現 錯誤 是否 重復 nlogn 獲得 不讓 字典 col

上午在做一個題,結果怎麽也做不出來,然後看了篇博客,發現其在求最小的字典序的時候用到了RMQ算法。

問徐大佬這是個什麽東西,大佬說:你們肯定學過,這個東西1個月以前60級的就問過我了。

為了不讓學弟學妹落下,我決定還是學學RMQ算法吧。。

one 前言

RMQ(Range Minimum/Maximum Query),是指區間查詢最值的算法,我們對於長度為n的數列A,回答若幹詢問RMQ(A,i,j)(i,j<=n)是求在序列A中,在區間i,j中出現的最小/最大值。

two RMQ算法

對於該類型的問題我們首先想到的就是遍歷,時間復雜度為o(n),但當我們要查詢一組較大的數據時,我們就無法用該算法在有效的時間內進行查詢。so,我們應該找一個簡單快速的方法來處理這類問題。該問題我們可以用一種比較高效的在線操作(ST算法)來解決這個問題。所謂在線算法,是指每個用戶每輸入一個查詢便馬上處理一個查詢。該算法一般用較長的時間做預處理,待信息充足以後便可以用較少的時間回答每個查詢。ST(Sparse Table)算法是一個非常有名的在線處理RMQ問題的算法,它可以在O(nlogn)時間內進行預處理,然後在O(1)時間內回答每個查詢。

  1.預處理

在預處理的時候我們一般用動態規劃(dp)來解決。

設我們要求出數組A[i]的區間最值,f[i][j]數組是用來存放區間i~2^j的最值。

例如:

A數列為:3 2 4 5 6 8 1 2 9 7

f[1][0]表示從第一個數到第2^0個數的最大值 其實也就是3這個值。同理f[1][1]這個值就等於max(3,2)=3, f[1][2]=max(3,4,5,6)=6,f[1][3]=max(3,2,4,5,6,8,1,2)=8.

通過觀察我們發現f[i][0]表示的就是從第i個點到第i個點的最值,顯然就是他本身A[i]吧。(這樣我們就可以得知dp的初始值)

這樣dp的初始值,dp的狀態我們都已經有了,剩下的就是狀態轉移方程了。然而這個狀態轉移方程這樣才能得出呢??

我們把我們所要求的區間i~2^j劃分成兩個區間(兩段,因為f[i][j]一定是個偶數個數字),我們把它劃分成i~2^(j-1)-1與2^(j-1)~2^j這兩段區間(這樣我們就把這兩段區間都劃分成長度為2^(j-1))。

用上例說明,當i=1,j=3時就是3,2,4,5 和 6,8,1,2這兩段。F[i,j]就是這兩段各自最大值中的最大值。於是我們得到了狀態轉移方程F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])

我們接下來來看看這種做法的代碼實現

void RMQ(int x)//我們在這裏對 x長度的一個數組進行區間查詢最值 
{
    for
(int j=1;j<20;j++) for(int i=1;i<=x;i++) { maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]); minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1)]); } }

通過觀察上述代碼我們可以發現一個問題

在進行循環的時候外層循環的是j,內層循環的是i,這是為什麽呢?我們是否可以把它換過來呢??

答案顯然是不可以。

這就需要我們必須理解該動態轉移方程的意義。

動態轉移方程的意義是:我們對於一個序列,我們要先更新f[1][0],再更新f[2][0]這樣我們才能更新出f[1][1]的值,也就是說我們要先更新出所有f[i][0]的值,然後再兩個兩個的進行更新,更新出一兩個元素為長度的兩個元素的最值,再進行更新,獲得所有長度為4的最值。。。。

而如果我們把i放在外面,這樣我們進行循環的時候,我們依次求出的是f[1][0],f[1][1],f[1][2],f[1][3];表示從元素1一直更新到元素2^3,這裏的f[1][3]=max(max(a[1],a[2],a[3]),max(a[4],a[5],a[6],a[7])的值,然而,在這裏我們並沒有max(a[1],a[2],a[3]),max(a[4],a[5],a[6],a[7]))的值,這樣我們就無法得到f[1][3]的值,這樣顯然這種方法是錯誤的。

  2.詢問

假如我們需要查詢的區間為(i,j),那麽我們需要找到覆蓋這個閉區間(左邊界取i,右邊界取j)的最小冪(可以重復,比如查詢5,6,7,8,9,我們可以查詢5678和6789)。

因為這個區間的長度為j - i + 1,所以我們可以取k=log2( j - i + 1),則有:RMQ(A, i, j)=max{F[i , k], F[ j - 2 ^ k + 1, k]}。

舉例說明,要求區間[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 ^ 2 + 1, 2]) = max(F[2, 2],F[5, 2]);

在這裏我們也需要註意一個地方,就是<<運算符和+-運算符的優先級

比如這個表達式:5 - 1 << 2是多少?

答案是:4 * 2 * 2 = 16。所以我們要寫成5 - (1 << 2)才是5-1 * 2 * 2 = 1。

RMQ算法