揹包九講
一,01 揹包問題
題解:
二維程式碼:
f [ i ][ j ] 表示只考慮到前 i 個物品,且總體積恰好是 j 的情況下,總價值最大是多少
遞推式:
情況①:不選第 i 個物品,f [ i ][ j ] =f [ i - 1 ][ j ]
情況②:選第 i 個物品,f [ i ][ j ] =f [ i - 1 ][ j - v[i] ] + w[ i ]
f [ i ][ j ] = max(①,②)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 1010 int n, m; // 物品數,揹包體積 int f[N][N]; int v[N], w[N]; // 物品體積,物品價值 int max(int a, int b) { return a > b ? a : b; } int main(void) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d%d", &v[i], &w[i]); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { f[i][j] = f[i - 1][j]; // 預設不選 if (j >= v[i]) // 如果可以選,即揹包體積大於物品體積 f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]); } } printf("%d\n", f[n][m]); system("pause"); return 0; }
解析:
f[i][j] = f[i - 1][j]; // 預設不選 if (j >= v[i]) // 如果揹包體積大於物品體積 f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
上面這段程式碼就是從第 i-1 個物品的狀態,推導第 i 個物品的狀態的遞推式。相較於上文 “選於不選” 的判斷條件,這裡省略了 “不選“ 的判斷條件,並在可以選擇的時候,比較選與不選各自的價值。也可以理解為:不能選的話,只能不選;可以選的話,就要看選與不選哪個價值大咯。
二維壓縮為一維:01 揹包的空間優化
f [ j ] 表示 如果當前考慮的是 i 個物品,那麼在總體積恰好是 j 的情況下,只考慮到前 i - 1 個物品的總價值最大是多少
遞推式:從第 i-1 個物品的狀態,推匯出第 i 個物品的狀態
情況①:不選第 i 個物品,f [ j ] =f [ j ] ,(注意,雖然這裡等式左右一樣,但意義不一樣,等式左邊代表的是隻考慮到前 i 個物品,而等式右邊代表的是隻考慮到前 i - 1 個物品。下文會將這種從 只考慮到前 i - 1 個物品 到 只考慮到前 i 個物品 的變化叫重新整理。這也是為什麼第二層迴圈要從後往前迴圈的原因)
情況②:選第 i 個物品,f [ j ] =f[ j - v[i] ] + w[ i ],(注意,f [ j ]代表的是隻考慮到前 i 個物品,f[ j - v[i] ] 代表的是隻考慮到前 i - 1 個物品。下文會將這種從只考慮到前 i - 1 個物品 到只考慮到前 i 個物品 的變化叫重新整理。這也是為什麼第二層迴圈要從後往前迴圈的原因)
f [ i ][ j ] = max(①,②)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 1010 int n, m; // 物品數,揹包體積 int f[N]; int v[N], w[N]; // 物品體積,物品價值 int max(int a, int b) { return a > b ? a : b; } int main(void) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d%d", &v[i], &w[i]); for (int i = 1; i <= n; i++) for (int j = m; j >= v[i]; j--) f[j] = max(f[j], f[j - v[i]] + w[i]); printf("%d\n", f[m]); system("pause"); return 0; }
解析:
f[j] = max(f[j], f[j - v[i]] + w[i]);
上面這段程式碼就是從第 i-1 個物品的狀態,推導第 i 個物品的狀態的遞推式。因為不選是 f[ j ] = f[ j ],沒有變化,所以雖然這裡沒有表示出不選的程式碼,但實際上還是有這個含義的。再來說一下不選,f[ j - v[ i] ] + w[ i ],問題就出在這個 f[ j - v[ i] ] 身上,因為如果我們 j 是正常的從小到大遞增的話,那麼 f[ j ] 肯定 還沒經過重新整理,但f[ j - v[ i] ] 就一定經過重新整理,所以此時f[ j - v[ i] ] 代表的是 “只考慮到前 i 個物品”,而不是我們需要的 “只考慮到前 i - 1 個物品”。那麼如何在一次 j 迴圈中,保證用到的f[ j - v[ i] ] 都沒有重新整理過呢?只能是從後往前迴圈了。♪(^∀^●)
所以,內層迴圈從二維壓縮到一維就是:
for (int j = m; j >= 0; j--) { f[j] = f[j]; // 不選 if(j >= v[i]) // 可以選,比較選與不選的價值 f[j] = max(f[j], f[j - v[i]] + w[i]); }
進一步優化就是:
for (int j = m; j >= v[i]; j--) f[j] = max(f[j], f[j - v[i]] + w[i]);
二,完全揹包問題
三,多重揹包問題
四,混合揹包問題
五,二維費用的揹包問題
六,分組揹包問題
七,揹包問題求方案數
八,求揹包問題的方案
九,有依賴的揹包問題
========== ========= ======== ====== ====== ===== ==== === == =