- 題目描述:
-
給定一個數字序列,查詢任意給定區間內數字的最小值。
- 輸入:
-
輸入包含多組測試用例,每組測試用例的開頭為一個整數n(1<=n<=100000),代表數字序列的長度。
接下去一行給出n個數字,代表數字序列。數字在int范圍內。
下一行為一個整數t(1<=t<=10000),代表查詢的次數。
最后t行,每行給出一個查詢,由兩個整數表示l、r(1<=l<=r<=n)。
- 輸出:
-
對於每個查詢,輸出區間[l,r]內的最小值。
- 樣例輸入:
-
5 3 2 1 4 3 3 1 3 2 4 4 5
- 樣例輸出:
-
1 1 3
RMQ (Range Minimum/Maximum Query)問題是指:對於長度為n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j里的最小(大)值,也就是說,RMQ問題是指求區間最值的問題。
主要方法及復雜度如下:
1、朴素(即搜索),O(n)-O(qn) online。
2、
線段樹,O(n)-O(qlogn) online。
3、ST(實質是
動態規划),O(nlogn)-O(q) online。
ST算法(Sparse Table),以求最大值為例,設d[i,j]表示[i,i+2^j-1]這個區間內的最大值,那么在詢問到[a,b]區間的最大值時答案就是max(d[a,k], d[b-2^k+1,k]),其中k是滿足2^k<=b-a+1(即長度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
d的求法可以用動態規划,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。
4、RMQ標准算法:先規約成
LCA(Lowest Common Ancestor),再規約成約束RMQ,O(n)-O(q) online。
首先根據原
數列,建立
笛卡爾樹,從而將問題在線性時間內規約為LCA問題。LCA問題可以在線性時間內規約為約束RMQ,也就是數列中任意兩個相鄰的數的差都是+1或-1的RMQ問題。約束RMQ有O(n)-O(1)的在線解法,故整個算法的時間復雜度為O(n)-O(1)。
下面是方法3的實現:
1 #include <climits> 2 #include <cmath> 3 #include <cstdio> 4 #include <iostream> 5 using namespace std; 6 7 int n, t; 8 int l, r; 9 int dp[100001][32]; 10 11 void init() { 12 for (int j = 1; (1<<j) <= n; ++j) { 13 for (int i = 1; i + (1<<j) - 1 <= n; ++i) { 14 dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]); 15 } 16 } 17 } 18 19 void getRes() { 20 int k = (int) (log((double)(r-l+1))/log(2.0)); 21 int res = min(dp[l][k], dp[r-(1<<k)+1][k]); 22 printf("%d\n", res); 23 } 24 25 int main() { 26 while (scanf("%d", &n) != EOF) { 27 for (int i = 1; i <= n; ++i) { 28 scanf("%d", &dp[i][0]); 29 } 30 init(); 31 scanf("%d", &t); 32 for (int i= 0; i < t; ++i) { 33 scanf("%d %d", &l, &r); 34 getRes(); 35 } 36 } 37 return 0; 38 } 39 /************************************************************** 40 Problem: 1544 41 User: hupo250 42 Language: C++ 43 Result: Accepted 44 Time:320 ms 45 Memory:14068 kb 46 ****************************************************************/
