dp

dynamic programming

動態規劃

費式數列

\(f(x) = f(x - 1) + f(x - 2)\)

直接遞迴好像很慢

把算過的用陣列存起來

一些名詞

狀態

在某種情況下的解

dp[x]是費氏數列第x項

轉移

幾種不同狀態之間的關係

\(dp[x] = dp[x - 1] + dp[x - 2]\)

AtCoder DP Contest

對於一個可以走的格子,一定是從左邊或上面走過來的

定義\(dp[i][j]\)為走到\((i,j)\)的方法數

  • 如果\((i, j)\)不能走,\(dp[i][j] = 0\)
  • 如果可以走,\(dp[i][j] = dp[i - 1][j] + dp[i][j - 1]\)

背包問題

D-Knapsack 1

直接把重量定成狀態

定義\(dp[i][j]\)代表目前有前i個物品,背包放到重量為j的最大價值

第i個物品的重量為\(w[i]\), 價值為\(v[i]\)

想一下怎麼轉移

一個物品可以分成要選或不選

一個物品可以分成要選或不選

一個物品可以分成要選或不選

不選

不會用到第i個物品

只會使用前i-1個來達到重量j

\(dp[i][j]=dp[i - 1][j]\)

一個物品可以分成要選或不選

不選

不會用到第i個物品

只會使用前i-1個來達到重量j

\(dp[i][j]=dp[i - 1][j]\)

要用到第i個物品

前i-1個總重量只能是\(j-w[i]\)

會拿到的價值是

\(dp[i - 1][j - w[i]] + v[i]\)

\(dp[i][j] = dp[i - 1][j - w[i]] + v[i]\)

一個物品可以分成要選或不選

不選

\(dp[i][j]=dp[i - 1][j]\)

\(dp[i][j] = dp[i - 1][j - w[i]] + v[i]\)

要選價值最高的

\(dp[i][j] = max(dp[i - 1][j], dp[i - 1][j-w[i]]+v[i])\)

時間複雜度

設背包最大重量為\(W\)

時間複雜度

設背包最大重量為\(W\)

有\(n\times W\)個狀態 => \(O(nW)\)

每個狀態的轉移分成要拿或不拿 => \(O(1)\)

時間複雜度

設背包最大重量為\(W\)

有\(n\times W\)個狀態 => \(O(nW)\)

每個狀態的轉移分成要拿或不拿 => \(O(1)\)

\(O(nW) \times O(1) = O(nW)\)

時間複雜度\(O(nW)\)

空間複雜度

\(O(nW)\)

優化

看\(i\)的時候只需要\(i - 1\)的狀態

所以只需要記\(i\)和\(i-1\)

空間複雜度\(O(W)\)

再優化

只記一個陣列,每次從上到下轉移

\(W\)很大,\(O(nW)\)會TLE

v很小

可以把v定成狀態

無限背包

\(dp[i][j] = max(dp[i - 1][j], dp[i][j-w[i]]+v[i])\)

每種物品都有無限多個

直接從i轉移

一樣可以做空間優化

直接從下到上轉移

有限背包

第i種物品有a[i]個

暴力作法:

每種物品都拆成a[i]個物品

做01背包

區間DP

把區間範圍定成狀態

dp[i][j]表示切一個從第i個切割點到第j個切割點之間的棍子的最小成本

(i跟j點是邊界,不能切)

把區間範圍定成狀態

dp[i][j]表示切一個從第i個切割點到第j個切割點之間的棍子的最小成本

(i跟j點是邊界,不能切)

選擇切割點

如果選擇k點當作切割點(\(i<k<j\))

則成本為\(a[j] - a[i]+dp[i][k]+dp[k][j]\)

                                原本棍子長度         剩餘的棍子切割成本

狀態壓縮DP

用dp狀態紀錄已經走過的點

可以用二進位的方式記錄走過的點的集合

dp[S][i]代表已走過S裡的所有點,且現在在i點

dp[S][i]代表已走過S裡的所有點,且現在在i點

所能得到的最短路徑

用dp狀態紀錄已經走過的點

可以用二進位的方式記錄走過的點的集合

dp[S][i]代表已走過S裡的所有點,且現在在i點

所能得到的最短路徑

舉例

\(n = 5, dp[01101][2]\)

走過{1, 2, 4}的點,且現在在2

dp[S][i]

轉移可以從S中選一個當作前一個點

選到的點要可以走到i

dp[i][S]:

前i個男生已經匹配,S集合內的女生也已經匹配

dp[i][S]:

前i個男生已經匹配,S集合內的女生也已經匹配

轉移

每次決定第i個男生要跟誰配對

dp[i][S]:

前i個男生已經匹配,S集合內的女生也已經匹配

轉移

每次決定第i個男生要跟誰配對

複雜度

\(O(n^2 2^n)\)

dp[i][S]:

前i個男生已經匹配,S集合內的女生也已經匹配

轉移

每次決定第i個男生要跟誰配對

複雜度

\(O(n^2 2^n)\)

可以更快嗎?

很顯然的

男生已經匹配數量 = 女生已經匹配數量

很顯然的

男生已經匹配數量 = 女生已經匹配數量

所以只需要數S裡面有多少個1

就可以知道i是多少

i就可以省略掉

複雜度

\(O(n2^n)\)

單調對列優化

給一個長度為n的序列a

請輸出對於\(1 \leq i \leq n\) 

區間max(0, i - k)到i的最小值

如果有一個\(j < i且a[j] \geq a[i]\)

那j在之後都沒有用了

首先讓i從1跑到n

Made with Slides.com