1. 程式人生 > 實用技巧 >RMQ問題(區間最值查詢)

RMQ問題(區間最值查詢)

RMQ(區間最值查詢)是指這樣一個問題:給定一個數組 ,其中有N個數字,給定區間[l ,r],詢問在這個區間內的最值。

這種的解決方法:暴力,線段樹(效率較低)

所以我們給出一種效率較高的演算法:st演算法,該演算法能在O(nlogn)的預處理後達到O(1)的查詢效率。

核心思路

1.預處理(dp):

假設陣列arr為:1,2,6,8,4,3,7

我們設二維陣列dp[i][j]表示從第i位開始連續個數中的最小值。例如dp[2][1]就表示從第二位數開始連續兩個數的最小值(也就是從第二位數到第三位數的最小值),即2,6中的最小值,所以dp[2][1] = 2;

其實我們求 dp[i][j] 的時候可以把它

分成兩部分,第一部分是從到,第二部分從到,為什麼可以這麼分呢?其實我們都知道二進位制數前一個數是後一個的兩倍,那麼可以把到這個區間通過分成相等的兩部分,那麼轉移方程很容易就寫出來了。(dp[i][0]就表示第i個數字本身)

dp[i][j] = min(dp [i][j - 1], dp [i + (1 << j - 1)][j - 1])

程式碼實現:

 1 void rmq_init(int arr[], int N) { //dp是全域性陣列,N指一共N個數
 2     for(int i = 1; i <= N; i++) {
 3         dp[i][0] = arr[i]; 
4 } 5 for(int j = 1; (1<<j) <= N; j++) { //注意ij順序 6 for(int i = 1; i+(1<<j)-1 <= N; i++) { 7 dp[i][j] = dp[i][j-1] == dp[i+(1<<(j-1))][j-1]; //先更新每兩個元素中的最小值,然後通過每兩個元素的最小值獲得每4個元素中的最小值,依次類推更新所有長度的最小值。 8 } 9 } 10 }

2.查詢部分:

1 int rmq(int
l, int r) { 2 int k = log2(r-l+1); //c語言math.h標頭檔案 3 return min(dp[l][k], dp[r-(1<<k)+1][k]); 4 }

只寫一個dp[][]只能查詢2的n次方個數,若要查詢的數的個數不是2的指數個則無法精準查詢(比如查詢5個數),故用

dp[l][k], dp[r-(1<<k)+1][k]以包含整個查詢區間。