# 動態規劃(Dynamic Programming) - <span>空間換取時間<!-- .element: class="fragment" data-fragment-index="1" --></span> - <span>將運算過且會重複利用到的值存起來<!-- .element: class="fragment" data-fragment-index="2" --></span> --- ## 費氏數列 - $F_0=0$ - $F_1=1$ - $F_n=F_{n-1}+F_{n-2} (2 \le n)$ ---- ## Bottom-up ```cpp= int x; long long dp[51]; dp[0] = 0; dp[1] = 1; for (int i = 2; i <= 50; i++) dp[i] = dp[i - 1] + dp[i - 2]; cin >> x cout << dp[x]; ``` ---- ## Top-down ```cpp= long long dp[51]; int F(int n) { if (n == 0) return 0; if (n == 1) return 1; if (dp[n] != 0) return dp[n]; return dp[n] = F(n - 1) + F(n - 2); } int main() { int x; cin >> x; cout << F(x); return 0; } ``` ---- ![](https://i.imgur.com/hOqpUnj.gif) --- ## 0/1背包問題 <span>將一群物品儘量塞進背包裡面,令背包裡面的物品總價值最高。背包沒有容量限制,無論物品是什麼形狀大小,都能塞進背包;但是背包有重量限制,如果物品太重,就會撐破背包。<!-- .element: class="fragment" data-fragment-index="1" --></span> ---- <!-- .slide: data-transition="zoom-in none-out" --> ### 範例 | 物品 | 價值 | 體積 | | --- | ---- | -- | | A | 5 | 11 | | B | 3 | 10 | | C | 2 | 7 | 體積限制:7 1. <span>按照CP值取<!-- .element: class="fragment" data-fragment-index="1" --></span> 2. ---- <!-- .slide: data-transition="none" --> ### 範例 | 物品 | 價值 | 體積 | | --- | ---- | -- | | A | 5 | 11 | | B | 3 | 10 | | C | 2 | 7 | 體積限制:7 1. ~~按照CP值取~~ 2. <span><!-- .element: class="fragment highlight-red" --> <span>窮舉<!-- .element: class="fragment" data-fragment-index="1" --></span> </span> ---- ### 優化! - 由於每個物品分為取或不取 → 計算次數約為$2^n$ - <span>動態規劃!!(運算次數為 $n \cdot w_{max}$)<!-- .element: class="fragment" data-fragment-index="1" --></span> ---- ### 動態規劃 令陣列$dp[i][j]$為在 *選取第$i$項物品* 時, *$選取物品重量 \le j$* 的最大價值。 - $1 \le i \le n$ - $1 \le j \le w_{max}$ 且$v[i]$為第$i$項的價值,$w[i]$為第$i$項的重量 ---- 經過思考後,可以推出以下轉移式: $dp[i][j]=$ $\left\{ \begin{array}{l} dp[i-1][j] \text{................................................}(j < w[i])\\ max(dp[i - 1][j], dp[i-1][j-w[i]]+v[i]\text{..}(j \ge w[i])\end{array} \right .$ ---- ### 解說 $dp[i][j] = max(dp[i - 1][j],\text{ } dp[i - 1][j - w[i]] + v[i])$ 1. <span>不選取: $dp[i - 1][j]$代表的是第$i$項不選取時 *$總重量 \le j$* 的最大值<!-- .element: class="fragment" data-fragment-index="1" --></span> 2. <span> 選取: $dp[i - 1][j - w[i]] + v[i]$選取此物品時 *$總重量 \le j$* 的最大值<!-- .element: class="fragment" data-fragment-index="2" --></span> <span>舉例:$i=3, j=10, w[i]=6, v[i]=5$。<!-- .element: class="fragment" data-fragment-index="3" --></span> <span>則$i-1=2, \text{ }j-w[i]=4$。<!-- .element: class="fragment" data-fragment-index="4" --></span> <span>$dp[i-1][j-w[i]]+v[i] = dp[2][4]+5$。<!-- .element: class="fragment" data-fragment-index="5" --></span> ---- ### ZeroJudge d637 第一行有正整數$n(1 < n \le 10000)$表有n顆鴨飼料 接下來的$n$行,每行有$ab$兩個正整數, $a$代表這顆鴨飼料的體積,$b$代表飽足感$(1 \le a \le 100 , 1 \le b \le 5000)$ 並且鴨子最多可以吃滿100體積的飼料。 請輸出最大飽足感為何。 ---- - 範例輸入: ``` 6 10 8 25 25 65 75 25 29 25 17 15 20 ``` - 範例輸出: ``` 112 (選取第1,3,4項) ``` ---- #### 程式碼 ```cpp= #include <iostream> using namespace std; const int maxn = 10000 + 1; const int w_max = 100; int dp[maxn][w_max + 1], w[maxn], v[maxn], n, ans; int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> w[i] >> v[i]; for (int i = 1; i <= n; i++) { for (int j = 1; j < w[i]; j++) dp[i][j] = dp[i - 1][j]; for (int j = w[i]; j <= w_max; j++) dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]); } cout << dp[n][w_max]; return 0; } ``` ---- #### 補充 根據以上程式碼,可以透過「滾動」的技巧來降低陣列大小,因只會存取到$dp[i]和dp[i-1]$,更以前的值不需要 則可令 $\left\{ \begin{array}{l} dp[i]=dp[i\text{%}2]=dp[i\text{&}1]\\ dp[i-1]=dp[(i-1) \text{%}2] =dp[!(i\text{&}1)]\end{array} \right .$ ---- 則程式碼可變成如下: ```cpp= #include <iostream> using namespace std; const int maxn = 10000 + 1; const int w_max = 100; int dp[2][w_max + 1], w[maxn], v[maxn], n, ans; int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> w[i] >> v[i]; for (int i = 1; i <= n; i++) { bool now = i & 1; bool prev = !now; for (int j = 1; j < w[i]; j++) dp[now][j] = dp[prev][j]; for (int j = w[i]; j <= w_max; j++) dp[now][j] = max(dp[prev][j], dp[prev][j - w[i]] + v[i]); } cout << dp[n & 1][w_max]; return 0; } ```
{"metaMigratedAt":"2023-06-14T19:26:57.825Z","metaMigratedFrom":"YAML","title":"動態規劃(Dynamic Programming)","breaks":true,"slideOptions":"{\"transition\":\"zoom\"}","contributors":"[{\"id\":\"2308ee1b-d046-4851-80ab-090ecbeaf503\",\"add\":9068,\"del\":4732}]"}
    694 views