01背包问题


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,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值