1,设dp[i][j]表示从下标[0~i]的物品中任取,放到容量为j的背包中的最大总价值;
2,转移方程:(两种情况)
2.1,背包放不下物品 i 时(此时物品i的重量大于背包容量j)
dp[i][j] = dp[i - 1][j];
2.2,背包可以放下物品 i 时
dp[i][j] = dp[i - 1][j - wight[i]] + value[i];
//dp[i - 1][j - wight[i]]是背包不放i时的最大价值
3,初始化
3.1,当 j 为零时,即背包容量为零,此时什么物品都放不下,所以价值为零,即dp[i][0] = 0;
3.2,当 i 为零时,即背包在不同容量下放编号为0的物品的最大价值,此时需要一个循环进行初始化;
for (int j = bagSize; j >= weight[0]; j–) {
dp[0][j] = dp[0][j - weight[0]] + value[0];
}
这里就有一个关键点了,为什么这里循环要用倒序遍历呢?
如果正序的话就会出现一个问题,在遍历过程中0号物品会被加很多次,因为每次不同容量下的背包 j 的最大价值总需要加上前一个容量 j-wight[0] 的背包的最大价值,这就造成了value[0]被重复相加,而我们只想每个容量下的value[0]只会加一次(可以举个例子试试)
而倒序就不会出现这个问题,因为从最大的开始,前一个最大价值总为0,所以只需要加上当前物品的最大价值value[i]即可;
总的来说就是要保证物品0在不同容量下只被放入一次
4,遍历顺序
最后我们需要考虑一下到底是先遍历物品 i 还是先遍历背包重量 j ?
其实这里谁先谁后都可以,没有什么区别;
代码如下:
int bagProblem1(vector weight, vector value, int bagsize) {
vector<vector> dp(weight.size() + 1, vector(bagsize + 1, 0));
for (int j = bagsize; j >= weight[0]; j–) {
dp[0][j] = dp[0][j - weight[0]] + value[0];
}
for(int i = 1; i < weight.size(); i++) { // 遍历每一件物品
for(int j = 0; j <= bagsize; 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]);
}
}
return dp[weight.size() - 1][bagsize]
}
当然这个代码对空间还可以优化一下,运用一个滚动数组,将dp二维降到一维,下面我们看看实现方式;
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);从中我们可以发现每次我们只需要dp[i][j]和dp[i - 1][j],即可以写成
dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);这时这个二维数组是不是就可以化为一维数组了;
接下来我们开始分析:
1,dp[j]表示容量为 j 的背包,所背的物品价值可以最大为dp[j]。
2,转移方程:
这时dp[j]有两个选择,一个是取自己dp[j],一个是取dp[j - weight[i]] + value[i],所以转移方程就是:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
3,初始化:
当背包容量 j 为零时,放不下任何东西,所以最大价值为0,