背包问题= =

文章详细介绍了01背包问题,通过动态规划的方法解决。分析了dp数组的定义、递推公式,以及初始化和遍历顺序。重点讨论了如何从物品和背包容量两个维度进行遍历,以求得在不超过背包容量的前提下,物品总价值的最大化。

一、01背包

N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

i件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。(下图是例子,一下分析均由此图得来)

1、分析:

(1) 确定dp数组以及下标的含义

对于背包问题,有⼀种写法, 是使⽤⼆维数组,即dp[i][j] 表⽰从下标为[0-i]的物品⾥任意

取,放进体积为j的背包(即总体积不超过j),价值总和最⼤是多少。

dp[i][j]:表示所有选法集合中,只从前i个物品中选,并且总体积≤j的选法的集合,它的值是这个集合中每一个选法的最大值.

(2)划分集合、确定递推公式

划分集合:选不选物品i

不选i:由dp[i - 1][j]推出,即背包容量为j,⾥⾯不放物品i的最⼤价值,此时dp[i][j]就是

dp[i - 1][j]

选i:由dp[i - 1][j - w[i]]推出,dp[i - 1][j - w[i]] 为背包容量为j - w[i]的时

候不放物品i的最⼤价值,那么dp[i - 1][j - w[i]] + value[i] (物品i的价值),

就是背包放物品i得到的最⼤价值

所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

(3)dp数组的初始化

关于初始化,⼀定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。

⾸先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],⽆论是选取哪些物品,背包

价值总和⼀定为0。如图:

在看其他情况。

状态转移⽅程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1推导出来,那么i为0的时候就⼀定要初始化。

dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最⼤价值。

那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量⽐编号0的物品重量还

⼩。

当j >= weight[0]是,dp[0][j] 应该是value[0],因为背包容量放⾜够放编号0物品。

代码初始化如下:

// 当然这⼀步,如果把dp数组预先初始化为0了,这⼀步就可以省略,但很多同学应该没有想清楚这⼀点。
for (int j = 0 ; j < weight[0]; j++) dp[0][j] = 0;

// 正序遍历
for (int j = weight[0]; j <= bagWeight; j++)  dp[0][j] = value[0];

此时dp数组初始化情况如图所⽰:

其实从递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出dp[i][j] 是由左上⽅数值推导出来的,那么 其他下标初始为什么数值都可以,因为都会被覆盖。但只不过⼀开始就统⼀把dp数组统⼀初始为0,更⽅便⼀些。

(4)确定遍历顺序

在如下图中,可以看出,有两个遍历的维度:物品与背包重量

那么问题来了,先遍历物品还是先遍历背包重量呢?

其实都可以!! 但是先遍历物品更好理解。

先遍历物品,然后遍历背包重量的代码:

// 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]; // 这个是为了展现dp数组⾥元素的变化
       else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
    }
}

先遍历背包,再遍历物品,也是可以的!(注意我这⾥使⽤的⼆维dp数组)

2、代码:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

21RGHLY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值