DP
什麼是 DP
Dynamic Programming
動態規劃
拿小問題的解推到大問題的解
怎麼用 DP
- 定狀態
- 推轉移
- 看邊界
定狀態
- 通常是某個子問題的解,例如求費氏數列第 n 項,定義就是 dp[i] = 第 i 項的值
- 這一步通常是最難想到的,也是大部分 DP 問題的第一步
- 多做題培養感覺
推轉移
- 有了狀態之後,問題就是要如何由小問題推到大問題的解了
- 思考方式像是大問題跟小問題差在哪,或是大問題要怎麼直接從小問題加上數學關係得到
- 例如費氏數列的轉移就是 dp[i] = dp[i-1] + dp[i-2]
看邊界
小問題就用手算(X
算好的東西要怎麼記錄
DP 的陣列,也會被稱為 DP 表格
例如上面的 dp[i] 就是一種
也有人會用 dfs+unordered_map/map
但是僅限於特殊情況,因為遞迴常數很大
使用時機
問題的最佳解可以從子問題的最佳解得到
不能有迴圈
估複雜度
總時間 = \(\displaystyle\sum_{狀態} 轉移量\)
剛開始通常每個狀態時間恆定,所以可以改成
狀態數量 \(\times\) 每個狀態的轉移量
題單在這,現在下課
你看到右邊藍藍的箭頭了嗎
看到了嗎
真的看到了嗎
TJ 時間
假設 dp[i] 表示青蛙從左邊開始跳,剛好跳到第 i 個石頭的最小花費
dp[i] 會跟那些東西有關?
考慮上一個跳過來的地方是誰,只有 i-1 與 i-2 兩種可能
那麼 dp[i] = min(dp[i-1] + abs(h[i] - h[i-1]), dp[i-2] + abs(h[i] - h[i-2]))
邊界條件 dp[0] = 0
複雜度 O(n)
定義方式同左
一樣是考慮 dp[i] 會跟那些東西有關
用一個迴圈掃過前 k 個更新 DP 值
複雜度 O(nk)
dp[i][j] = 考慮到第 i 個,第 i 個剛好選的是 j 的最大值
因為第 i 個要選誰只跟前一個是誰有關
轉移式 dp[i][j] = max(dp[i-1][],dp[i-1][]) + a[i]
複雜度 O(n)
這題的 w 比較大,但是 v 比較小
不能用 O(nw) 的做法
反著考慮:假設當前已經拿了總價值恰好為 v 的東西,最小重量是多少
轉移方式類似,邊界條件是 dp[0] = 0, dp[i] = inf
經典題
叫做 longest common subsequence (LCS)
設 dp[i][j] = s 的前 i 項與 t 的前 j 項的 LCS 是多少
如果 s[i] == t[j],那麼在 dp[i-1][j-1] 後面增加一個字元 s[i] 一定是最好的
否則 dp[i][j] = max(dp[i-1][j], dp[i][j-1])
複雜度 O(ST)
dp[i][j] 表示走到 (i,j) 點有幾種走法
如果走不到就設成 0
dp[i][j] = ...
考慮 dp[i][j][k] 表示有 i 個 1,j 個 2,k 個 3
轉移可能會有自環 -> 移項
後面還有但我懶得放了
CSES 也值得去刷
DP
By alvingogo
DP
- 757