1. 程式人生 > >RMQ (Range Minimum/Maximum Query)演算法

RMQ (Range Minimum/Maximum Query)演算法

RMQ演算法是一種查詢一個區間最值的演算法,當然是有Q次詢問,如果只詢問一次,當然直接遍歷就好了,

如果是詢問很多次,這時就需要RMQ演算法了。

RMQ演算法

RMQ演算法用的是DP求解, 預處理是nlogn的,查詢是O(1)。

A[i]表示要查詢的數列,F[i,j]表示從i開始2^j個數中最大的那一個。

例如:            3 ,4 ,5,23,1,12,7

F[1,0]就表示從3開始的1個數,就是3本身,F[2,2]表示從4開始2^2個數,即4,5,23,1這四個數中最大值23.

那麼DP的初始狀態是什麼?

就是每個數的最大值是其本身,即F[i,0],把F[i,0]先求出來就是DP的初態

那麼DP的狀態轉移方程呢?

通過二分的思想很容易得出,F[i,j]=max(F[i,j-1],F[i+(1<<(j-1)),(j-1)].

即把從i開始的2^j個數分成兩個區間,分別是 [ i,i+2^(j-1)-1 ]和[ i+2(j-1),i+2^j-1],找出這兩個區間中最大值進行比較。

void rmq(int n)
{
    for(int i=1;i<20;i++)
        for(int j=1;j+(1 << i)-1<=n;j++)    //j代表從j開始分為(j,j+2^(i-1)-1),(j+2^(i-1),j+2^i)這兩個區間比較
        {
            maxnum[j][i]=max(maxnum[j][i-1],maxnum[j+1<<(i-1)][i-1]);
            minnum[j][i]=min(minnum[j][i-1],minnum[j+1<<(i-1)][i-1]);
        }
}

需要注意的問題:

1、注意for迴圈的巢狀,不能把j那一層放到外面,如果放到外面,就成了先求

  F[1,1],F[2,1],F[3,1]......

給出一個序列:3 ,4 ,5,23,1,12,7

F[1,1]=4,F[2,1]=5,F[3,1]=23,乍一看好像也沒什麼問題,接下來繼續迴圈

F[1,2]=4,F[2,2]=5,F[3,2]=23,應該看出來問題來了, 找從i開始的4個數,只有前一半是更新出最值來了,後一半還是0.

所以不能這樣迴圈。必須先把每個位置的值都從j=1(2個數的最值)開始依次更新,再是j=2(4個數的最值),3(8個數的最值)......

2、注意一下<< 和+,-運算子的優先順序(在這踩了好長時間的坑。。。)

例如 1+2<<2

答案是3*2*2=12,所以想要表達成我們想要的算式則是:1+(2<<2).

查詢過程

可以看成指數的逆運算(我是這麼理解的),例如[l,r]這個區間,一共有r-l+1個數,那麼就是k=log2(r-l+1),

那麼要求的最大值就是 max([l,k],[r-(1<<k)+1,k],

最小值同理。

while(q--){
        scanf("%d%d",&l,&r);
        int k=(int)(log(r-l+1.0)/log(2.0));
        int maxn=max(maxnum[l][k],maxnum[r-(1<<k)+1][k]);
        int minx=min(minnum[l][k],minnum[r-(1<<k)+1][k]);
        printf("%d\n",maxn-minx);
    }