KB專題:區間DP專輯
自從多校後心憔悴啊,發現DP還是太水了,有一場的區間DP竟然不會做,咳,果然是赤裸裸的水軍。
花了幾天時間寫了幾道區間DP的題目,大部分都是水題,然後和以前的合併起來就是KB區間DP這個8 + 1道題的專輯,大家可以試著AK。
區間DP是一類在區間上進行動態規劃的最優問題,一般是根據問題設出一個表示狀態的dp,可以是二維的也可以是三維的,一般情況下為二維。然後將問題劃分成兩個子問題,也就是一段區間分成左右兩個區間,然後將左右兩個區間合併到整個區間,或者說區域性最優解合併為全域性最優解,然後得解。
這類DP可以用常規的for迴圈來寫,也可以用記憶化搜尋來寫,個人更傾向於記憶化搜尋寫法,因為這種寫法相當易懂,要什麼值你直接去記憶化搜尋一下就ok了,隨叫隨到隨用啊。
1、ZOJ 3537 Cake
第一次寫的 凸包結合 DP,調了好久,
問題屬於經典的三角剖分。
區間DP狀態轉移方程:
\[f[i][j] = min(f[i][k] + f[k][j] + cost[i][k] + cost[k][j] \]其中 \(j >= i+ 3,i+1<=k<=j-1,cost[i][k]\) 為連一條 \(i\) 到 \(k\) 的線的費用
詳細題解:Here
2、LightOJ 1422 Halloween Costumes
本題不可以直接模擬棧是因為,不知道當前的衣服本來就有還是需要新穿一件。
初始化DP為一直穿衣服
即
for (int i = 1; i <= n; ++i) for (int j = i; j <= n; ++j) f[i][j] = f[i][j - 1] + (j == i || a[j] != a[j - 1]);
然後從按區間DP板子,列舉 \(k\) ,只要 \(a[k]\) 與 \(a[j]\) 相同說明,是可以把從 \(k+1\) 到 \(j-1\) 的所有衣服都脫掉,然後 \(k\) 天的這件衣服,直接在第 \(j\) 天參加聚會,就不需要穿新的衣服。從小區間依次遞推到大區間,就可以得到正確的答案。
\[f[i][j] = f[i][j - 1] (a[i] == a[j]) \\ f[i][j] = min(f[i][k] + f[k + 1][j]) (a[i] == a[k]\ and\ i\le k\le j) \]const int N = 110; int a[N], n, Case, f[N][N]; int main() { cin.tie(nullptr)->sync_with_stdio(false); int _; for (cin >> _; _--;) { cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; for (int i = 1; i <= n; ++i) for (int j = i; j <= n; ++j) f[i][j] = f[i][j - 1] + (j == i || a[j] != a[j - 1]); for (int len = 1; len <= n; ++len) for (int i = 1; i + len <= n; ++i) { int j = i + len; for (int k = i; k + 1 <= j; ++k) if (a[k] == a[j]) f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j - 1]); else f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j - 1] + 1); } cout << "Case " << ++Case << ": " << f[1][n] << "\n"; memset(f, 0, sizeof(f)); } }
3、POJ 2955 Brackets
4、CF 149 D Coloring Brackets
5、1651 Multiplication Puzzle
7、Hdu 4283 You Are the One (較難)
8、Sdut 不老的傳說問題 (較難)
The desire of his soul is the prophecy of his fate
你靈魂的慾望,是你命運的先知。