01背包问题总结

01背包问题

背包问题是经典的动态规划问题,而01背包基本算得上是其他背包问题的基础,01背包问题即有一个有固定容量的背包,用这个背包去装有固定大小和固定价值的一些物品,问怎么装(装哪些物品)能让背包内价值总和最大。因为每个物品要么装要么不装,对应1和0,所以又叫01背包问题。

二维dp

首先01背包可以用最普通的动态规划解决,首先dp[i] [j]代表在容量为j的情况下,在0~i个物品中选择使得背包价值最大。对于第i个物品,我们可以选择装或者不装,主要是判断这两种情况哪种使得背包价值最大,即

dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i])。

然后是初始化,如果nums = {1,2,3,4},容量为10,我们会把dp数组的大小设为dp[nums.size()] [11],即从0号物品,从容量为0起。初始化要做的事情就是把容量为0对应的dp数组赋值为0,把0号物品对应的dp数组按能否装下赋值。

dp[nums.size()][bagcapacity] = {0};
for (int j = weight[0]; j <= bagcapacity; j++) {
	dp[0][j] = value[0]
}
// 本来还要把装不下第0号物品的重量置为0以及把重量0对应的置为0,但是数组所有元素已经初始化为0,所以初始化只需要把能装下第0号物品
// 的重量置为相应价值

然后就是确定遍历顺序了,先遍历容量再遍历物品或者先遍历物品再遍历容量都行。例如

// weight数组的大小 就是物品个数
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
    for(int i = 1; i < weight.size(); i++) { // 遍历物品
        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]);
    }
}
// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); 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]);

    }
}

最后dp[nums.size() - 1] [capacity]即为最大价值。

一维dp

上面二维dp转移方程为:dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i])。其实如果dp[i - 1] [j]能够赋给dp[i] [j]的话,那么公式就变成了

dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);

那不如就直接用一维数据了。

dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

此时dp[j]表示当容量为j时最大的价值。

那函数就可写成

for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
}

这里有两点需要注意的地方:

  1. 遍历背包容量要从大到小,否则会出现把同一个物品放进去两次的情况。如果背包容量是从小到大,那么dp[j - weight[i]]中可能已经包含了i对应的价值,再加上value[i]就相当于放进去了两次。
  2. 要先遍历物品,再遍历背包容量,因为推导dp[j]的时候要用到dp[j]~ dp[0](减去weight[i]后会落在这个范围中),如果先遍历背包,又因为1的原因,dp[j]~ dp[bagweight]先求出来了,但是dp[0]~dp[j - 1]还没有求出来,就出错了。

上面两个地方可能不会特别好懂,但是动态规划终究还是填dp这个表,我们可以把这个表画出来,自己填一填,看看每个元素需要用到哪些元素就知道了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值