问题描述:
有n
件物品和一个最多能背重量为w
的背包。第i件物品的重量是weight[i]
,得到的价值是value[i]
。每件物品只能用一次,求解将哪些)物品装入背包里物品价值总和最大。
二维dp数组01背包
1. 确定dp数组以及下标的含义
dp[i][j]
表示从下标为[0-i]
任意组合放入背包后,背包容纳重量为j
时的最大价值
2. 确定递推公式
有两个方向推出来dp[i][j]
:
-
不放物品i:由
dp[i - 1][j]
推出,即背包容量为j
,里面不放物品i
的最大价值,此时dp[i][j]
就是dp[i - 1][j]
。(其实就是当物品
i
的重量大于背包j
的重量时,物品i
无法放进背包中,所以背包内的价值依然和前面相同。) -
放物品i:由
dp[i - 1][j - weight[i]]
推出,dp[i - 1][j - weight[i]]
为背包容量为j - weight[i]
的时候不放物品i的最大价值,
那么dp[i - 1][j - weight[i]] + value[i]
(物品i的价值),就是背包放物品i得到的最大价值j - weight[i]
: 不装物品i时的容量dp[i - 1][j - weight[i]]
:装i
时前j - weight[i]
容量的最大价值。
当i
放进去时,那么这时候整个物品集就被分成两部分,1到i-1
和第i个,而这时i
是确定要放进去的,那么就把j
空间里的weight[i]
给占据了,只剩下j-weight[i]
的空间给前面i-1
,那么只要这时候前面i-1
在j-weight[i]
空间里构造出最大价值,即dp[i-1][j-weight[i]]
,再加上此时放入的i的价值value[i]
,就是dp[i][j]
了
递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
dp[i][j] = max(不放物品,放物品)
不装
i
即需要到前i-1
个里面选,也就是前**i-1
行j
背包容量下的最大价值**,由于前面都已经是最优解,直接取dp[i - 1][j]
就是不装i
条件下的最大价值
for (int i = 1; i < weight.size(); i++){
// 遍历物品
for (int j = 1; 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]);
}
}
}
为什么需要if (j < weight[i])
?
因为:当j-weight[i] < 0
时,数组会越界,即 dp[i - 1][j - weight[i]]
会报错
3. dp数组如何初始化
从dp[i][j]
的定义出发:
如果背包容量j
为0
的话,即dp[i][0]
,无论是选取哪些物品,背包价值总和一定为0。
如果i=0
的情况就是只有