RMQ用於區間快速查找最值,適用於期間數值無更改的情況。其預處理的複雜度爲O(nlogn),查詢的時間複雜度爲O(1),對比於線段樹的預處理O(nlogn),查詢O(logn)來說,在某些情況下有着其獨到的優勢。
RMQ原理就是在原來的數組上跑一個dp,我們以查詢最大值爲例,它的狀態定義是這樣的:
dp[ i ][ j ]:下標從i開始,長度爲2^j的區間的最大值。顯然dp[ i ][ 0 ]就是下標是i的那個數字本身。
下面給出其轉移方程:
dp[ i ][ j ] = max( dp[ i ][ j - 1 ], dp[ i + 2 ^ j ][ j - 1 ] )
對於詢問區間[ i ~ j ]的最大值Max:
設k = log( j - i + 1 ) / log( 2 )
Max = max( dp[ i ][ k ], dp[ j - 2 ^ k + 1 ][ k ] )
上述過程的具體代碼如下:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
#include <ctime>
#include <cmath>
#include <set>
#define eps 1e-10
#define MAXN 500010
#define INF 2*0x3f3f3f3f
using namespace std;
int num[MAXN], dp[MAXN][30], n, l, r;
int pow(int a, int p) { //求a^p這裏用了快速冪,實際用應該用一個數組預處理一下
if (p == 0) return 1;
int ans = pow(a, p / 2);
ans *= ans;
if (p % 2) ans *= a;
return ans;
}
int main() {
//freopen("in.in", "r", stdin);
//freopen("out.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &num[i]);
for (int i = 1; i <= n; i++) //對dp[i][0]進行初始化
dp[i][0] = num[i];
for (int j = 1; pow(2, j) <= n; j++) //上文說的轉移方程
for (int i = 1; i + pow(2, j) - 1 <= n; i++)
dp[i][j] = max(dp[i][j - 1], dp[i + pow(2, j - 1)][j - 1]);
scanf("%d %d", &l, &r); //求區間[l~r]之間的最大值
int k = log(r - l + 1) / log(2);
int ans = max(dp[l][k], dp[r - pow(2, k) + 1][k]);
printf("ans is : %d\n", ans);
return 0;
}
RMQ問題在處理LCA中有着巨大的用處,其一種在線方法就是使用dfs+RMQ來求兩個子節點的最近公共祖先問題,其大致做法就是按照訪問的順序把每個點的時間戳放入一個數組中,這樣u和v的公共祖先就是數組中u和v之間時間戳最小的點,這裏可以之間用RMQ在O(1)的時間內得到答案了。