完全揹包
問題
有N種物品和一個容量是V的揹包,每種物品都有無限件可用。
第i種物品的體積是vi,價值是wi。
求解將哪些物品裝入揹包,可使這些物品的總體積不超過揹包容量,且總價值最大。
輸出最大價值。
輸入格式
第一行兩個整數,N,V,用空格隔開,分別表示物品種數和揹包容積。
接下來有N行,每行兩個整數vi,wi,用空格隔開,分別表示第i種物品的體積和價值。
輸出格式
輸出一個整數,表示最大價值。
資料範圍
0<N,V≤1000
0<vi,wi≤1000
定義f(i, j)表示前i個物品任意選擇不限次數總體積不大於j情況下的最大總價值,遞迴關係終點f(i, 0) = 0;
針對第i個物品,可以選擇k次,那麼狀態方程為:
f(i, j) = max { f(i - 1, j - k * vi) + k * wi } k>=0 1⃣️
= max ( f(i - 1, j), max { f(i - 1, j - k * vi) + k * wi } ) k>=1 2⃣️.可以考慮如果第i件物品一次不選的情況,就等於前i-1件物品的選擇
= max ( f(i - 1, j), max { f(i - 1, j - vi - k * vi) + k * wi + wi } ) k>=0 3⃣️.令k = k + 1
= max ( f(i - 1, j), f(i, j - vi) + wi ) 4⃣️.根據1⃣️公式推導過來,將j - vi 看作一個整體
#include <iostream> #include <string> #include <algorithm> using namespace std; const int N = 1001; int n,m; int p[N][N]; int v[N], w[N]; int main() { cin >> n >> m; for (int i = 1; i <= n; i ++) { cin >> v[i] >> w[i]; } for (int i = 1; i <= n; i ++) { for (int j = 0; j <= m; j++) { p[i][j] = p[i - 1][j]; if (j >= v[i]) { p[i][j] = max(p[i][j], p[i][j - v[i]] + w[i]); } } } cout << p[n][m] << endl; return 0; }
同01揹包一樣,我們可以將二維陣列優化為一維資料。
f(i, j) = max ( f(i - 1, j), f(i, j - vi) + wi ) => f(j) = max ( f(j), f(j - vi) + wi )
此時我們需要保證max中 f(j)是上一個物品選擇的結果,f(j - vi)必須是當前物品已經計算過的選擇結果,那麼內層迴圈j就必須是從小到大來迴圈。
例如:第一個物品體積1,價值2,揹包總容量5;選擇第一個商品時,如果從大到小迴圈計算p[5] = max (p[5], p[3] + 2) = 2,此時p[3]=0是沒有選擇物品時的值;
但是實際上揹包容量是5的時候,我們可以將第一個物品放入兩次,所以計算p[5]時,一定要保證p[3]這個一定計算過放入第一個物品的結果。
#include <iostream> #include <string> #include <algorithm> using namespace std; const int N = 1001; int n,m; int p[N]; int v[N], w[N]; int main() { cin >> n >> m; for (int i = 1; i <= n; i ++) { cin >> v[i] >> w[i]; } for (int i = 1; i <= n; i ++) { for (int j = v[i]; j <= m; j++) { p[j] = max(p[j], p[j - v[i]] + w[i]); } } cout << p[m] << endl; return 0; }
如果要恰好裝滿揹包的話,同01揹包一樣,我們只要定義p[j] = -1,p[0] = 0即可。
#include <iostream> #include <string> #include <algorithm> using namespace std; const int N = 1001; int n,m; int p[N]; int v[N], w[N]; int main() { cin >> n >> m; for (int i = 1; i <= n; i ++) { cin >> v[i] >> w[i]; p[i] = -1; } for (int i = 1; i <= n; i ++) { for (int j = v[i]; j <= m; j++) { if (p[j - v[i]] != -1) { p[j] = max(p[j], p[j - v[i]] + w[i]); } } } if (p[m] != -1) { cout << p[m] << endl; } else { cout << "無法裝滿揹包" << endl; } return 0; }