1. 程式人生 > >51NOD1174 區間最大數 && RMQ問題(ST算法)

51NOD1174 區間最大數 && RMQ問題(ST算法)

pac cst %d for ron 介紹 nlog double sin

技術分享圖片

技術分享圖片

RMQ問題(區間最值問題Range Minimum/Maximum Query)

    ST算法

    RMQ(Range Minimum/Maximum Query),即區間最值查詢,是指這樣一個問題:對於長度為n的數列a,回答若幹詢問RMQ(A,i,j)(i, j<=n),返回數列a中下標在i,j之間的最小/大值。如果只有一次詢問,那樣只有一遍for就可以搞定,但是如果有許多次詢問就無法在很快的時間處理出來。在這裏介紹一個在線算法。所謂在線算法,是指用戶每輸入一個查詢便馬上處理一個查詢。該算法一般用較長的時間做預處理,待信息充足以後便可以用較少的時間回答每個查詢。ST(Sparse Table)算法是一個非常有名的在線處理RMQ問題的算法,它可以在O(nlogn)時間內進行預處理,然後在O(1)時間內回答每個查詢。

    假設a數組為:

    1, 3, 6, 7, 4, 2, 5

    1.預處理

    設dp[i][j]表示從第i位開始連續2^j個數中的最大值。例如dp[2][1]為第2位數開始連續2個的數的最大值,即6, 4之間的最大值,即mn[2][1] = 4。我們先初始化dp[0...n-1][0]為a數組中的值,之後我們很容易想到遞推方程:

    dp[i][j] = max(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1])


20000> 2**14 > 10000
30000> 2**15 > 40000
60000> 2**16 > 70000


1
for(int j = 0; j < 20; j ++) 2 for(int i = 1; i + (1 << j) <= n + 1; i ++) 3 dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);

    2.查詢

    假設我們需要查詢[lo, hi]之間的最值,我們令k = log2(hi-lo+1)

    由於k是 log2(hi-lo+1)向下取整得到的,所以無法完全覆蓋lo到hi

    只要保證lo + k 的長度大於lo到hi的長度的1/2即可使用:RMQ[l, r] = min(dp[l][k], dp[r - (1 << k) + 1][k]);

    技術分享圖片

    因為不難看出,對於任意長度len,(int)log2(len) ** 2 > len/2

    所以最終代碼如下:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6 /*
 7 20000> 2**14 > 10000
 8 30000> 2**15 > 40000
 9 60000> 2**16 > 70000
10 */
11 int N[10007];
12 
13 int mx[10007][15];
14 
15 int main()
16 {
17     int n,q;
18     scanf("%d", &n);
19     for(int i=0;i<n;i++)
20     {
21         scanf("%d", &N[i]);
22     }
23     // -------------------------------
24 
25     memset(mx, 0, sizeof(mx));
26     for(int i=0;i<n;i++)
27     {
28         mx[i][0] = N[i];
29     }
30 
31     for(int i=1;i<15;i++)
32     {
33         for(int j=0;j+(1<<i)-1<n;j++)
34         {
35             mx[j][i] = max(mx[j][i-1], mx[j+(1<<i-1)][i-1]);
36         }
37     }
38 
39     // -------------------------------
40     scanf("%d", &q);
41     int lo, hi;
42     for(int i=0;i<q;i++)
43     {
44         scanf("%d%d", &lo, &hi);
45         int k = (int)log2((double)(hi-lo+1));
46         int ans = max(mx[lo][k], mx[hi-(1<<k)+1][k]);
47         printf("%d\n", ans);
48     }
49 }

當然也可以用線段樹解決

51NOD1174 區間最大數 && RMQ問題(ST算法)