RMQ問題(區間最值查詢)
阿新 • • 發佈:2020-09-08
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(intl, 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]以包含整個查詢區間。