--- tags: algorithm --- # DP ## 背包問題 來看到`DP`的一個經典例題,背包問題。 #### 問題簡述 給予一個容量為`k`的背包。 和一些物品,每個物品都有其大小和價值。 問再不超過背包容量的條件下,能裝入的最大價值。 ### 0/1背包 每項物品都只有一個 背包容量為`M`,有`N`個物品。 $v[i] \implies 第i個物品的價值$ $w[i] \implies 第i個物品的重量$ #### DP解 ```cpp= vector<int> v(N), w(N); vector<vector<int>> dp(N, vector<int>(M + 1, 0)); for(int i = 0; i < N; i++) { for(int j = 0; j < w[i]; j++) dp[i][j] = dp[i - 1][j]; for(int j = w[i]; j <= M; j++) dp[i][j] = max(dp[i - 1][j], v[i] + dp[i - 1][j - w[i]]); } ``` #### 滾動DP ```cpp= vector<int> v(N), w(N); vector<int> dp(M + 1, 0); for(int i = 0; i < N ;i++) { for(int j = M; j >= w[i]; j--) dp[j] = max(dp[j], v[i] + dp[j - w[i]]); } ``` :::info Q: 為什麼要從後往前DP呢? ::: ### 無限背包 從前往後`DP`, 即可。 ```cpp= vector<int> v(N), w(N); vector<int> dp(M + 1, 0); for(int i = 0; i < N ;i++) { for(int j = w[i]; j <= M; j++) dp[j] = max(dp[j], v[i] + dp[j - w[i]]); } ``` --- ### 有限背包 `DP,位元運算` #### 一開始的想法 把每一個物品都做一次`O/1背包`。 ```cpp= vector<int> v(N), w(N), num(N); for(int i = 0; i < N;i ++) for(int t = 0; t < num[i]; t++) for(int j = M; j >= w[i]; j--) dp[j] = max(dp[j], v[i] + dp[j - w[i]]); ``` #### 優化 把物品的數量拆成二的倍數。 ```cpp= vector<int> v(N), w(N), num(N); for(int i = 0; i < N; i++) { int rest = num[i]; int weight, val; for(int t = 0; t <= rest; t <<= 1) { //t *= 2 rest -= t; weight = t * w[i], val = t * val[i]; for(int j = M;j >= weight; j--) dp[j] = max(dp[j], val + dp[j - weight]); } weight = t * w[i], val = t * v[i]; for(int j = M; j >=; j++) { dp[j] = max(dp[j], val + dp[j - weight]); } } ```