DP-2
戴瑢溱
Index
簡介
給定一組物品,每個物品都有自己的「重量」與「價值」,在限定的總重量(W)中,選擇哪些物品裝入背包,能使背包內的總價值(V)最大化?
不超重的情況下 (總重量 <= W) 最大的總價值 !




限重:W
重量:W1
:
價值:V1
重量:W2
價值:V2
重量:W3
價值:V3
給定一組物品,每個物品都有自己的「重量」與「價值」,在限定的總重量(W)中,選擇哪些物品裝入背包,能使背包內的總價值(V)最大化?




限重:W
0/1背包問題





限重:W
重量:W1
:
價值:V1
重量:W2
價值:V2
重量:W3
價值:V3
物品只能選「拿(1)」或「不拿(0)」,且一種物品只有一個





限重:5
重量:1
:
價值:100
重量:3
價值:200
重量:4
價值:300





| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
(1, 100) (3, 200) (4, 300)



















| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
(1, 100) (3, 200) (4, 300)
不放任何東西,總價值皆為0



















限重為0時,總價值皆為0
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | ||||||
| 0 | ||||||
| 0 |
(1, 100) (3, 200) (4, 300)



















放入一個手套,總價值為100
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | |||||
| 0 | ||||||
| 0 |
(1, 100) (3, 200) (4, 300)



















較大的背包一樣能放入手套,總價值皆為100
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | ||||||
| 0 |
(1, 100) (3, 200) (4, 300)



















限重 < 3的背包只能放手套不能放手電筒,總價值為100
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | ||||
| 0 |
(1, 100) (3, 200) (4, 300)



















限重為3的背包可以放手電筒,價值為200
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | |||
| 0 |
(1, 100) (3, 200) (4, 300)



















一個手套 + 一個手電筒,價值為300
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 |
(1, 100) (3, 200) (4, 300)



















以此類推…
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 |
(1, 100) (3, 200) (4, 300)



















一個手套 + 一個玩具飛機,總價值400
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















We did it !
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)




















這些數字彼此間有什麼關係呢?
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)




















演算法在尋找最佳解的時候比較兩個選項

| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















A. 新物品的價值,加上剩餘空間能換到的最大價值
B. 不拿新物品,沿用不放這個物品時的最佳解
演算法在尋找最佳解的時候比較兩個選項

A. 新物品的價值,加上剩餘空間能換到的最大價值
B. 不拿新物品,沿用不放這個物品時的最佳解
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















100 + 0
0
演算法在尋找最佳解的時候比較兩個選項

A. 新物品的價值,加上剩餘空間能換到的最大價值
B. 不拿新物品,沿用不放這個物品時的最佳解
200 + 100
100
| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















演算法在尋找最佳解的時候比較兩個選項

| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















A. 新物品的價值,加上剩餘空間能換到的最大價值
B. 不拿新物品,沿用不放這個物品時的最佳解
300 + 100
300

dp[i+1][j]=max(dp[i][j], dp[i][j-w[i]]+v[i]); | 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false); // 加速輸入輸出
cin.tie(0);
int m, n;
while (cin >> m >> n) {
vector<int> w(m), v(m);
for (int i = 0; i < m; i++) {
cin >> w[i] >> v[i];
}
// 宣告 vector 二維 dp 表格, 初始化為 0
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 0; i < m; i++) {
for (int j = 0; j <= n; j++) {
if (w[i] <= j) {
// dp[i+1] 代表當前物品,dp[i] 代表前一個狀態
dp[i + 1][j]
= max(dp[i][j], dp[i][j - w[i]] + v[i]);
} else {
dp[i + 1][j] = dp[i][j];
}
}
}
cout << dp[m][n] << endl;
}
return 0;
}
但是二維陣列好大一個,
我的背包很大很佔空間欸 !

搗蛋鬼,別搗蛋 !
害我又要多學一個,
好吧我們來試試看一維陣列 !

dp[j] = max(dp[j], dp[j - w[i]] + v[i]);| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)




















for (int j = n; j >= w[i]; j--)| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















由後往前更新,能確保當前計算 dp[j] 時,所參考的 dp[j - w[i]] 依然是上一輪留下的結果,等同於二維表格中的「上一列」

for (int j = n; j >= w[i]; j--)| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















如果由前往後更新,後面的重量會參考到「這輪已經放過物品」的狀態,導致同一個物品被重複放入(變成完全背包問題)
#include <bits/stdc++.h>
using namespace std;
int main() {
int m, n;
while (cin >> m >> n) {
int w[m], v[m];
for (int i = 0; i < m; i++)
cin >> w[i] >> v[i];
// 空間優化:只需長度為 n+1 的一維陣列
vector<int> dp(n + 1, 0);
for (int i = 0; i < m; i++) {
// 由後往前遍歷
for (int j = n; j >= w[i]; j--) {
// dp[j] = max(原本不放的值, 放了新物品後的價值)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
// 提示:當 j < w[i] 時,dp[j] 會維持原值,不需額外處理
}
cout << dp[n] << endl;
}
return 0;
}完全背包問題
限重:W
重量:W1
:
價值:V1
重量:W2
價值:V2
重量:W3
價值:V3
每一樣物品都有好幾個,可以重複放!






dp[j] = max(dp[j], dp[j - w[i]] + v[i]);| 物品\重量 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | |
| 0 | 100 | 100 | 100 | 100 | 100 | |
| 0 | 100 | 100 | 200 | 300 | 300 | |
| 0 | 100 | 100 | 200 | 300 | 400 |
(1, 100) (3, 200) (4, 300)



















#include <bits/stdc++.h>
using namespace std;
int main() {
int m, n;
while (cin >> m >> n) {
int w[m], v[m];
for (int i = 0; i < m; i++)
cin >> w[i] >> v[i];
vector<int> dp(n + 1, 0);
for (int i = 0; i < m; i++) {
// 正序遍歷,從重量 w[i] 往上加到 n
for (int j = w[i]; j <= n; j++) {
// dp[j-w[i]] 可能是已經放過第 i 種物品後的最佳值
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
cout << dp[n] << endl;
}
}kahoot !
DP-2
By ariel tai
DP-2
- 55