DP

什麼是 DP

Dynamic Programming
動態規劃

拿小問題的解推到大問題的解

 怎麼用 DP 

  1. 定狀態
  2. 推轉移
  3. 看邊界

定狀態

  1. 通常是某個子問題的解,例如求費氏數列第 n 項,定義就是 dp[i] = 第 i 項的值
  2. 這一步通常是最難想到的,也是大部分 DP 問題的第一步
  3. 多做題培養感覺

推轉移

  1. 有了狀態之後,問題就是要如何由小問題推到大問題的解了
  2. 思考方式像是大問題跟小問題差在哪,或是大問題要怎麼直接從小問題加上數學關係得到
  3. 例如費氏數列的轉移就是 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

  • 744