01背包
01背包
使用01背包能得到限定条件下的最优价值。
01背包的第一要点是一般有两个状态量,一是背包容量,二是价值。二者都是根据题目需要定义出来的。相应的,要装入“背包”的“物体”具有重量和价值。
dp[i][j],w[i],v[i]
(dp[i][j]为在第i个物体下背包容量为j的最大价值,w[i]为物体质量,v[i]为物体价值。)
如何将物体装入背包,就涉及到01背包的第二个要点——递推时决策当前物体装还是不装。
如果装这个物体,那么就要做到当前背包总容量是刚好满的(贪心条件下容量越满装的价值必定越多 ),
所以就要从上一个物体递推的结果中,找到背包装的质量加上要装的物体的质量刚好等于当前背包容量。
dp[i-1][j-w[i]]+v[i]
(但是要注意j要大于等于当前物体质量)
这只是得到了装这个物体时能得到的最大的结果,还要判断这个价值够不够大,可以不可以覆盖上一个物体在这个背包质量的值(假设现在求价值最大可以为多少)。
如果这个值不够大,就选择不装,直接将上一个物体在这个背包质量的值挪过来填进去。
两个步骤可以用一个语句完成:
dp[i][j]=max(dp[i][j],d[i-1][j-w[i]]+v[i]);
最终核心代码如下:
for(int i=1;i<=n;i++){
for(int j=w[i];j<=m;j++){
dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]);
}
}
如果发现使用一个二维数组空间太大了,可以把dp[i][j]优化成dp[j],因为递推时只需回溯到当前物体的上一个物体,那么一维的数组就足够往下递推了
核心代码如下
for(int i=1;i<=n;i++){
for(int j=m;j>=w[i];j--){
dp[j]=max(dp[[j-w[i]]+v[i],dp[j]);
}
}
要注意一维数组和二维数组在第二个for循环中j的顺序刚好相反
for(int j=w[i];j<=m;j++)//二维数组正序
for(int j=m;j>=w[i];j--)//一维数组逆序
原因如下
二维数组正序:每个 dp[i][j] 仅依赖上一行数据,同行的计算互不干扰需要回溯方案
一维数组逆序:防止 dp[j-w] 被当前轮的更新覆盖(若正序会重复选物品)仅求最大价值
选择递推物体的顺序可以不用管
因为无论先遍历哪个物品,动态规划会穷举所有可能的组合,最终 dp[i][j] 必然收敛到全局最优解。中间状态可能不同,
但结果一致,就像先喝汤在吃饭与先吃饭再喝汤的关系,顺序不同但总共还是吃这么多

浙公网安备 33010602011771号