1. 程式人生 > 其它 >C/C++ 記憶體申請函式的區別

C/C++ 記憶體申請函式的區別

範圍最小值問題(Range Minimum Query, RMQ)。給出一個n個元素的陣列A,1 A2....An,設計一個數據結構,支援查詢操作Query(L,R):計算mIn{AL,A(L+1).....AR};

每次用一個迴圈來計算最小值顯然不夠快,字首和的思想也不能提高效率(想- -想, 為什麼),怎麼辦呢?實踐中最常用的是Tarjan和Sparse-Table演算法,它預處理的時間是O(nlogn),但是查詢只需要0(1),而且常數很小。最重要的是,這個演算法非常好寫,並且不易寫錯。

令d(i,j)表示從i開始的,長度為2^j的一段元素中的最小值,則可以用遞推的方法計算d(i,j): d(i,j)=min{d(i,j -1), d(i+2^(j-1),j 1)},原理如圖所示。

其實我們求 dp[i][j] 的時候可以把它分成兩部分,第一部分是從i到 i+2j-1-1 ,第二部分從 i+2j-1 到 i+2j-1,為什麼可以這麼分呢?其實我們都知道二進位制數前一個數是後一個的兩倍,那麼可以把 i 到i+2 j-1 這個區間通過2 j-1分成相等的兩部分, 那麼轉移方程很容易就寫出來了。

(dp[i][0]就表示第i個數字本身)

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

因此程式碼如下

點選檢視程式碼
void rmq_init()
{
    for(int i=1;i<=N;i++)
        dp[i][0]=arr[i];//初始化
    for(int j=1;(1<<j)<=N;j++)
        for(int i=1;i+(1<<j)-1<=N;i++)
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
}

這裡需要注意一個迴圈變數的順序,我們看到外層迴圈變數為j,內層迴圈變數為i,這是為什麼呢?可以互換一下位置嗎?

答案當然是不可以,我們要理解這個狀態轉移方程的意義,這個狀態方程的含義是:先更新每兩個元素中的最小值,然後通過每兩個元素的最小值獲得每4個元素中的最小值,依次類推更新所有長度的最小值。

查詢部分程式碼:

點選檢視程式碼
int rmq(int l,int r)
{
    int k=log2(r-l+1);
    return min(dp[l][k],dp[r-(1<<k)+1][k]);
}