有 N N N 件物品和一个最多能背重量为 W W W 的背包。第 i i i 件物品的重量是 w e i g h t [ i ] weight[i] weight[i],得到的价值是 v a l u e [ i ] value[i] value[i]。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
确定dp数组以及下标的含义
使用二维数组
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示从下标[0-i]的物品里任意取,放进容量为 j 的背包中的最大价值总和。
确定递推公式
动态规划的思想是从上一状态推出当前状态,从
d
p
[
i
−
1
]
[
j
]
dp[i-1][j]
dp[i−1][j] 状态来推
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],即在 $dp[i-1][j]
状态下考虑物品
状态下考虑 物品
状态下考虑物品i$ 的取舍:
dp数组初始化
从定义出发,当背包的重量为0时,无法选取任何物品,即对于
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]来说全部等于0;
当仅考虑物品0时,只有当背包的重量大于
w
e
i
g
h
t
[
0
]
weight[0]
weight[0]时才能选取物品0,即对于
d
p
[
0
]
[
j
]
dp[0][j]
dp[0][j]来说,当
j
<
w
e
i
g
h
t
[
0
]
j
//声明dp数组,并初始化所有元素=0
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
//初始化do[0][j]
for (int i = weight[0]; i <= bagWeight; i++) {
dp[0][i] = value[0];
}
确定遍历顺序
由递推公式来看,先遍历物品和先遍历背包均可以,但是先遍历物品更好理解。
举例推导
int bag01() {
vector<int> weight = { 1, 3, 4 };
vector<int> value = { 15, 20, 30 };
int bagWeight = 4;
int wsize = weight.size();
int vSize = value.size();
vector<vector<int>> dp(wsize + 1, vector<int>(bagWeight + 1, 0));
//初始化
for (int i = weight[0]; i <= bagWeight; i++) {
dp[0][i] = value[0];
}
//遍历
for (int i = 1; i < wsize; i++) {
for (int j = 0; j <= bagWeight; j++) {
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
cout << dp[wsize - 1][bagWeight] << endl;
return 0;
}