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] 必然收敛到全局最优解。中间状态可能不同,

但结果一致,就像先喝汤在吃饭与先吃饭再喝汤的关系,顺序不同但总共还是吃这么多

posted @ 2025-05-22 01:33  洛鸿  阅读(15)  评论(0)    收藏  举报