• 01背包详解


    01背包总结


    背包可以不装满

    题干:

    Weight[N] , Value[N], backSize

    解释:

    有N件物品,重量存放在Weight[]数组中,价值存放在Value[]数组中。即第 i i i件物品对应的重量为 W e i g h t [ i ] Weight[i] Weight[i] ,价值为 V a l u e [ i ] Value[i] Value[i]

    ​ 在背包最多承受的重量为 b a c k S i z e backSize backSize每件物品最多使用一次的情况下, 背包的最大价值是多少。

    二维dp状态转移方程

    d p [ i ] [ k ] = max ⁡ { d p [ i − 1 ] [ k ] ( 前 i − 1 件物品放入背包 ) d p [ i − 1 ] [ k − W e i g h t [ i ] ] + V a l u e [ i ] ( 前 i 件物品放入背包 ) dp[i][k] = \max{

    {dp[i1][k](i1)dp[i1][kWeight[i]]+Value[i](i)" role="presentation" style="position: relative;">{dp[i1][k](i1)dp[i1][kWeight[i]]+Value[i](i)
    } dp[i][k]=max{dp[i1][k]dp[i1][kWeight[i]]+Value[i](i1件物品放入背包)(i件物品放入背包) 01背包二维状态转移方程

    解释:

    ​ 乍一看方程会有些懵,我来解释一下 d p [ i ] [ k ] dp[i][k] dp[i][k]的含义

    i : 将前 i 件物品放入背包 i : 将前i件物品放入背包 i:将前i件物品放入背包

    k : 此时背包的容量为 k k : 此时背包的容量为k k:此时背包的容量为k

    d p [ i ] [ k ] : 前 i 件物品在背包最大重量为 k 的情况下的最大价值 dp[i][k]:前i件物品在背包最大重量为k的情况下的最大价值 dp[i][k]:i件物品在背包最大重量为k的情况下的最大价值

    接着看两个选项

    ​ $dp[i-1][k] : $ 根据上面的解释 不难带入理解:前(i-1)件物品在背包最大重量为k时的最大价值

    d p [ i − 1 ] [ k − W e i g h t [ i ] ] + V a l u e [ i ] : dp[i-1][k-Weight[i]]+Value[i] : dp[i1][kWeight[i]]+Value[i]: Value[i] 是 选择了将第 i 件物品放入背包, 那么此时背包最多还能放 (k-Weight[i])这么重的物品,而这些物品我们需要在前(i-1)件物品中选择

    那么能不能优化一下空间呢?当然可以。

    优化二维dp:

    在上面的例子中,每一行只跟上一行有关系,那么是否可以使用一维数组达到二维数组的效果呢?

    哈哈答案当然是可以的。

    假设 d p dp dp 数组是一个长度为 N + 1 N+1 N+1 的一维数组(可以将这里的 d p dp dp数组想象成上面二维数组 d p [ i ] [ k ] dp[i][k] dp[i][k] 中的任意一行)

    我们呢知道:在更新 d p [ m ] [ n ] dp[m][n] dp[m][n]所在的一行时,在二维 d p [ i ] [ k ] dp[i][k] dp[i][k]中,与这行相关的只有 d p [ m − 1 ] [ n ] dp[m-1][n] dp[m1][n]所在的这一行,也就是 d p [ m ] [ n ] dp[m][n] dp[m][n] 的上一行。

    具体见 01背包二维状态转移方程

    所以 使用一维 d p [ i ] dp[i] dp[i] 来计算是完全可能的

    那么 此时 如何解释 d p [ i ] dp[i] dp[i] 呢?

    i : i: i: 背包最大重量为 i i i

    d p [ i ] : dp[i]: dp[i]: 在背包最大重量为 i i i 的情况下的最大价值

    一维dp状态转移方程:

    d p [ k ] = max ⁡ { d p [ k ] ( 在不取第 i 件物品情况下的价值 ) V a l u e [ i ] + d p [ k − W e i g h t [ i ] ] ( 在取第 i 件物品情况下的价值 ) dp[k] = \max{

    {dp[k](i)Value[i]+dp[kWeight[i]](i)" role="presentation" style="position: relative;">{dp[k](i)Value[i]+dp[kWeight[i]](i)
    } dp[k]=max{dp[k]Value[i]+dp[kWeight[i]](在不取第i件物品情况下的价值)(在取第i件物品情况下的价值) 01背包一维状态转移方程

    一维dp 的重点在于遍历背包容量时的顺序

    我们来看一个例子

    std::vector<int> Weight = {2,2};
    std::vector<int> Value = {3,5};
    std::vector<int> dp(7,0);
    
    • 1
    • 2
    • 3

    遍历第一个物品

    我们希望得到的 d p dp dp 数组

    dp[0]dp[1]dp[2]dp[3]dp[4]dp[5]dp[6]
    0033333

    遍历第二个物品

    我们希望得到的 d p dp dp 数组

    dp[0]dp[1]dp[2]dp[3]dp[4]dp[5]dp[6]
    0055888

    假设我们已经遍历完了第一个物品 请记住现在 d p dp dp 数组 的状态

    此时 我们开始遍历第二个物品对应的 d p dp dp

    遍历方式:

    这时候我们有两种遍历方式 1. 从 d p [ 0 ] → d p [ 6 ] 从 dp[0]\to dp[6] dp[0]dp[6] 2. 从 d p [ 6 ] → d p [ 0 ] 从 dp[6]\to dp[0] dp[6]dp[0]

    我们来看一下有什么区别

    注意: 下面是 遍历第二件物品的情况

    1. 从 d p [ 0 ] → d p [ 6 ] 从 dp[0]\to dp[6] dp[0]dp[6] k k k 从 0 → \to 6 ,得到下面的 d p dp dp数组
    dp[0]dp[1]dp[2]dp[3]dp[4]dp[5]dp[6]
    0055101010
    1. 从 d p [ 6 ] → d p [ 0 ] 从 dp[6]\to dp[0] dp[6]dp[0] k k k 从 6 → \to 0 ,得到下面的 d p dp dp数组
    dp[0]dp[1]dp[2]dp[3]dp[4]dp[5]dp[6]
    0055888

    发现按照 从 d p [ 0 ] → d p [ 6 ] 从 dp[0]\to dp[6] dp[0]dp[6] 这种遍历背包容量的方式得到的结果并不正确

    分析一下原因:

    在遍历时,我们覆盖了背包某一容量的值 ,而这一容量值在更新之后的值时需要使用,错误的值导致了错误的结果。

    即 过早更新了 d p [ k − W e i g h t [ i ] ] dp[k-Weight[i]] dp[kWeight[i]] 导致 了错误。

    在上面的例子中 我们过早更新了 d p [ 2 ] dp[2] dp[2] 的值 ,之后我们就丢失了 上一件物品 的数据,之后在更新 d p [ 4 ] 、 d p [ 5 ] 、 d p [ 6 ] dp[4]、dp[5]、dp[6] dp[4]dp[5]dp[6] 时,都需要比较 d p [ 2 ] dp[2] dp[2] 的值。但是此时 d p [ 2 ] dp[2] dp[2]已经是本层的最大价值了,应该比较的是上一层的最大价值,这就导致了价值错误。

    所以为了保证在更新较大背包容量的最大价值时,使用正确的数据,应该从背包容量较大处开始比较。

    即采用 从 d p [ n ] → d p [ 0 ] 从 dp[n]\to dp[0] dp[n]dp[0]

    那么 dp[n] 即为所求。


    恰好装满:

    如果要求是恰好装满,只需在初始化 d p dp dp数组时,将 d p [ 0 ] dp[0] dp[0] 初始化为 0,其余初始化为 I N T _ M I N INT\_MIN INT_MIN

    std::vector<int> dp(n+1,INT_MIN);
    dp[0] = 0;
    
    • 1
    • 2

  • 相关阅读:
    装饰者模式
    文档丢失怎么找回?学会这3个方法就足够!
    Windows server 2012安装IIS的方法
    有人会画pcb吗,那个缝合孔用proteus怎么弄
    【AIGC专题】Stable Diffusion 从入门到企业级实战0401
    967亿销售额!博世解码智能汽车新蓝图
    【脚本工具】SVG路径中的A指令转DXF的圆弧和椭圆弧 & C++代码实现
    基本if选择结构以及random
    CSR/SSR以及同构渲染的区别
    第五届“强网”拟态防御国际精英挑战赛——特邀战队篇
  • 原文地址:https://blog.csdn.net/qq_33909269/article/details/133758919