RMQ算法讲解
哇!這題簡單啊,一個for循環,遍歷數組記錄最大值輸出即可啊。
?
那好,現在我告訴你假設N為50000,給你Q次詢問((1 ≤ Q ≤ 200,000)),如果這種情況,我們還每次都進行暴力遍歷求解的話,那么無論你提交幾萬次都會得到如下結果:
?
是的,這種暴力遍歷求解雖然思維簡單,代碼簡短,但是很慢啊。
?
那該怎么做呢?以前我也不會啊,自從學了RMQ,誒,真好用,我們全家都用它!
?
RMQ(Range Minimum/Maximum Query),即區間最值查詢。RMQ算法一般用較長時間做預處理,時間復雜度為O(nlogn),然后可以在O(1)的時間內處理每次查詢。
?
下面我們從一個實際問題來解釋RMQ
我們假設數組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])
由此給出下列代碼:
這里需要注意一個循環變量的順序,我們看到外層循環變量為j,內層循環變量為i,這是為什么呢?可以互換一下位置嗎?
?
答案當然是不可以,我們要理解這個狀態轉移方程的意義,這個狀態方程的含義是:先更新每兩個元素中的最小值,然后通過每兩個元素的最小值獲得每4個元素中的最小值,依次類推更新所有長度的最小值。
?
而如果是i在外,j在內的話,我們更新的順序就變成了從1開始的前1個元素,前2個元素,前4個元素,前8個元素。。。
當j等于3的時候dp[1][3] = min(min(ans[0],ans[1],ans[2],ans[3]),min(ans[4],ans[5],ans[6],ans[7])))的值,
但是我們根本沒有計算min(ans[0],ans[1],ans[2],ans[3])和min(ans[4],ans[5],ans[6],ans[7]),所以這樣的方法肯定是錯誤的。
為了避免這樣的錯誤,一定要好好理解這個狀態轉移方程所代表的含義。
?
接下來我們來講解RMQ的查詢部分,假設我們需要查詢區間[l ,r]中的最小值,令k = , 則區間[l, r]的最小值RMQ[l,r] = min(dp[l][k], dp[r - (1 << k) + 1][k]);
?
但是為什么這樣就可以保證是區間最小值了呢?
?
dp[l][k]維護的是區間 [l, l + 2^k - 1] , dp[r - (1 << k) + 1][k]維護的是區間 [r - 2^k + 1, r] 。
那么只要我們保證? ≤ 就能保證RMQ[l,r] = min(dp[l][k], dp[r - (1 << k) + 1][k]);
?
接下來我們用分析法來證明這個不等式:
我們假設 ? ≤ 這個等式成立
即有 r - l + 2 ≤ 也就是 r - l + 2 ≤
又因為 k = ;
那么 r - l + 2 ≤ 2 * (r - l +1)
則 r - l + 2 ≤ 2*(r - l) + 2
即 r - l ≤ 2*(r-l)
所以 r - l ≥ 0,即假設成立
我們舉個例子, l = 4,r = 6;
假設數組arr為:1,2,6,8,4,3,7
此時 k = = = 1
則區間[4,6]的最小值 = min(dp[4][1],dp[5][1])
dp[4][1] = 4,dp[5][1] = 3,所以區間[4,6]的最小值 = min(dp[4][1],dp[5][1]) = 3
我們很容易看出來答案是正確的。
由此給出查詢部分代碼:
好了,至此RMQ全部介紹完畢。
總結
- 上一篇: 泡腾片用热水还是冷水
- 下一篇: 霰粒肿是怎么引起的?