RMQ(範圍最小化問題)【倍增法】

問題描述

範圍最小化問題(Range Minimum Query,RMQ)。給出一個 n 個元素的數組 A1,A2,,AnA_1, A_2,\dots,A_n,設計一個數據結構,支持查詢 Query(L, R): 計算 minAL,AL+1,,ARmin{A_L, A_{L+1},\dots,A_R}

解題報告

dp[i][j]dp[i][j] 表示從 ii 開始的,長度爲 2j2^j 的一段元素中的最小值,則可以用遞推的方式計算 dp[i][j]dp[i][j]
dp[i][j]=min(dp[i][j1],dp[i+2j1][j1])dp[i][j]=min(dp[i][j-1], dp[i+2^{j-1}][j-1])
在這裏插入圖片描述
爲什麼 dp[i][j] 表示的是 從 ii 開始的,長度爲 2j2^j 的一段元素中最小值呢?
如果這樣的話,就需要在三個區間中找最小值,一方面狀態轉移方程不好寫,另一方面,時間複雜度和空間複雜度並沒有提升多少,所以是 2j2^j

注意到 2jn2^j\le n,因此 d 數組的元素個數不超過 nlognnlogn,而每一項都可以在常數時間計算完畢,故總時間爲 O(nlong)O(nlong)

查詢操作很簡單,令 k 爲滿足 2kRL+12^k\le R-L+1 的最大整數,則以 LL 開頭、以 RR 結尾的兩個長度爲 2k2^k 的區間合起來覆蓋了查詢區間 [L,R][L,R]。由於是取最小值,有些元素重複考慮了幾遍也沒有關係。

實現代碼

  • 預處理
void RMQ_INIT(const vector<int>&A){
	int n= A.size();
	for(int i=0;i<n;i++)dp[i][0]=A[i];
	for(int j=1;(1<<j)<=n;j++){
		for(int i=0;i+(1<<j)-1<n;i++)
			dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1])
	}
}
  • 查詢
int RMQ(int l, int r){
	int k=0;
	while((1<<(k+1))<=r-l+1) k++;
	return min(d[l][k], d[r-(1<<k)+1][k])
}

參考資料

[1] 算法競賽·入門經典·訓練指南 P197.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章