1. 程式人生 > 其它 >【演算法學習筆記】區間DP

【演算法學習筆記】區間DP

基本的知識點引用自 OI wiki,感謝社群的幫助

什麼是區間 DP?

區間類動態規劃是線性動態規劃的擴充套件,它在分階段地劃分問題時,與階段中元素出現的順序和由前一階段的哪些元素合併而來有很大的關係。令狀態 \(f(i,j)\) 表示將下標位置 \(i\)\(j\) 的所有元素合併能獲得的價值的最大值,那麼 \(f(i,j)=\max\{f(i,k)+f(k+1,j)+cost\}\)\(cost\) 為將這兩組元素合併起來的代價。

區間 DP 的特點:

合併:即將兩個或多個部分進行整合,當然也可以反過來;

特徵:能將問題分解為能兩兩合併的形式;

求解:對整個問題設最優值,列舉合併點,將問題分解為左右兩個部分,最後合併兩個部分的最優值得到原問題的最優值。

例題「NOI1995」石子合併

在一個環上有 $n$ 個數 $a_1,a_2,...,a_n$,進行 $n-1$ 次合併操作,每次操作將相鄰的兩堆合併成一堆,能獲得新的一堆中的石子數量的和的得分。你需要最大化你的得分。

正常來說考慮不在環上,而在一條鏈上的情況。

\(f(i,j)\) 表示將區間 \([i,j]\) 內的所有石子合併到一起的最大得分。

寫出 狀態轉移方程

\[f(i,j)=\max\{f(i,k)+f(k+1,j)+\sum_{t=i}^{j} a_t \}~(i\le k \]

\(sum_i\) 表示 \(a\) 陣列的字首和,狀態轉移方程變形為

\[f(i,j)=\max\{f(i,k)+f(k+1,j)+sum_j-sum_{i-1}\} \]

怎樣進行狀態轉移

由於計算 \(f(i,j)\) 的值時需要知道所有 \(f(i,k)\)\(f(k+1,j)\) 的值,而這兩個中包含的元素的數量都小於 \(f(i,j)\),所以我們以 \(len=j-i+1\) 作為 DP 的階段。首先從小到大列舉 \(len\),然後列舉 \(i\) 的值,根據 \(len\)\(i\) 用公式計算出 \(j\) 的值,然後列舉 \(k\),時間複雜度為 \(O(n^3)\)

怎樣處理環

題目中石子圍成一個環,而不是一條鏈,怎麼辦呢?

方法一:由於石子圍成一個環,我們可以列舉分開的位置,將這個環轉化成一個鏈,由於要列舉 \(n\) 次,最終的時間複雜度為 \(O(n^4)\)

方法二:我們將這條鏈延長兩倍,變成 \(2\times n\) 堆,其中第 \(i\) 堆與第 \(n+i\) 堆相同,用動態規劃求解後,取 \(f(1,n),f(2,n+1),...,f(i,n+i-1)\) 中的最優值,即為最後的答案。時間複雜度 \(O(n^3)\)

核心程式碼
for (len = 1; len <= n; len++)
    for (i = 1; i <= 2 * n - 1; i++) {
        int j = len + i - 1;
        for (k = i; k < j && k <= 2 * n - 1; k++)
            f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + sum[j] - sum[i - 1]);
    }


區間DP 練習題題解 ①:Here

區間DP 練習題題解 ②:Here

The desire of his soul is the prophecy of his fate
你靈魂的慾望,是你命運的先知。