当前位置: 首页 > ds >正文

详解0-1背包的状态转移表

初始理解0-1背包问题

首先,需要明确什么是0-1背包问题。0-1背包问题是一个经典的动态规划问题,其描述如下:

  • 给定一组物品,每个物品有一个重量(weight)和一个价值(value)。
  • 有一个容量有限的背包(capacity)。
  • 目标是在不超过背包容量的前提下,选择一些物品装入背包,使得背包中物品的总价值最大。
  • “0-1”意味着每个物品要么完整地选(1),要么不选(0),不能分割。

动态规划解决思路

动态规划通常用于解决具有重叠子问题和最优子结构性质的问题。对于0-1背包问题:

  1. 定义状态:通常使用一个二维数组 dp[i][w] 来表示考虑前 i 个物品,在背包容量为 w 时能够获得的最大价值。
  2. 状态转移方程
    • 对于第 i 个物品,有两种选择:
      • 不选dp[i][w] = dp[i-1][w](即最大价值与不考虑当前物品时相同)。
      • (只有在当前物品的重量 weight[i]w 时才可能):dp[i][w] = dp[i-1][w - weight[i]] + value[i]
    • 因此,状态转移方程为:
      dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight[i]] + value[i]) if weight[i] <= welse dp[i-1][w]
      
  3. 初始化
    • dp[0][w] = 0 对于所有 w,因为没有物品时最大价值为0。
    • dp[i][0] = 0 对于所有 i,因为背包容量为0时不能装任何物品。

构建状态转移表

为了更好地理解,我决定通过一个具体的例子来构建状态转移表。假设有以下物品:

  • 物品1:重量=2,价值=3
  • 物品2:重量=3,价值=4
  • 物品3:重量=4,价值=5
  • 物品4:重量=5,价值=6

背包的总容量 W = 8

物品列表
物品编号 (i)重量 (weight[i])价值 (value[i])
123
234
345
456
状态转移表 dp[i][w]

表格的行表示物品编号 i(从0到4,其中i=0表示没有物品),列表示背包容量 w(从0到8)。

初始化:

  • dp[0][w] = 0 for all w(没有物品时价值为0)。
  • dp[i][0] = 0 for all i(容量为0时不能装物品)。

现在逐步填充表格:

i=0 (没有物品):

w012345678
0000000000

i=1 (物品1: weight=2, value=3):

对于每个 w,考虑是否选择物品1:

  • w < 2: 不能选,dp[1][w] = dp[0][w] = 0
  • w >= 2:
    • 不选:dp[0][w] = 0
    • 选:dp[0][w-2] + 3 = 0 + 3 = 3
    • 取 max: max(0, 3) = 3
w012345678
1003333333

i=2 (物品2: weight=3, value=4):

对于每个 w

  • w < 3: dp[2][w] = dp[1][w]
  • w >= 3:
    • 不选:dp[1][w]
    • 选:dp[1][w-3] + 4
    • 取 max

计算:

  • w=0,1,2: dp[2][w] = dp[1][w] (0, 0, 3)
  • w=3:
    • 不选: dp[1][3] = 3
    • 选: dp[1][0] + 4 = 0 + 4 = 4
    • max(3,4) = 4
  • w=4:
    • 不选: dp[1][4] = 3
    • 选: dp[1][1] + 4 = 0 + 4 = 4
    • max(3,4) = 4
  • w=5:
    • 不选: dp[1][5] = 3
    • 选: dp[1][2] + 4 = 3 + 4 = 7
    • max(3,7) = 7
  • w=6:
    • 不选: dp[1][6] = 3
    • 选: dp[1][3] + 4 = 3 + 4 = 7
    • max(3,7) = 7
  • w=7:
    • 不选: dp[1][7] = 3
    • 选: dp[1][4] + 4 = 3 + 4 = 7
    • max(3,7) = 7
  • w=8:
    • 不选: dp[1][8] = 3
    • 选: dp[1][5] + 4 = 3 + 4 = 7
    • max(3,7) = 7
w012345678
2003447777

i=3 (物品3: weight=4, value=5):

对于每个 w

  • w < 4: dp[3][w] = dp[2][w]
  • w >= 4:
    • 不选: dp[2][w]
    • 选: dp[2][w-4] + 5
    • 取 max

计算:

  • w=0,1,2,3: dp[3][w] = dp[2][w] (0, 0, 3, 4)
  • w=4:
    • 不选: dp[2][4] = 4
    • 选: dp[2][0] + 5 = 0 + 5 = 5
    • max(4,5) = 5
  • w=5:
    • 不选: dp[2][5] = 7
    • 选: dp[2][1] + 5 = 0 + 5 = 5
    • max(7,5) = 7
  • w=6:
    • 不选: dp[2][6] = 7
    • 选: dp[2][2] + 5 = 3 + 5 = 8
    • max(7,8) = 8
  • w=7:
    • 不选: dp[2][7] = 7
    • 选: dp[2][3] + 5 = 4 + 5 = 9
    • max(7,9) = 9
  • w=8:
    • 不选: dp[2][8] = 7
    • 选: dp[2][4] + 5 = 4 + 5 = 9
    • max(7,9) = 9
w012345678
3003457899

i=4 (物品4: weight=5, value=6):

对于每个 w

  • w < 5: dp[4][w] = dp[3][w]
  • w >= 5:
    • 不选: dp[3][w]
    • 选: dp[3][w-5] + 6
    • 取 max

计算:

  • w=0,1,2,3,4: dp[4][w] = dp[3][w] (0, 0, 3, 4, 5)
  • w=5:
    • 不选: dp[3][5] = 7
    • 选: dp[3][0] + 6 = 0 + 6 = 6
    • max(7,6) = 7
  • w=6:
    • 不选: dp[3][6] = 8
    • 选: dp[3][1] + 6 = 0 + 6 = 6
    • max(8,6) = 8
  • w=7:
    • 不选: dp[3][7] = 9
    • 选: dp[3][2] + 6 = 3 + 6 = 9
    • max(9,9) = 9
  • w=8:
    • 不选: dp[3][8] = 9
    • 选: dp[3][3] + 6 = 4 + 6 = 10
    • max(9,10) = 10
w012345678
40034578910

完整的状态转移表

将上述所有行的 dp[i][w] 整合起来:

i \ w012345678
0000000000
1003333333
2003447777
3003457899
40034578910

解读表格

  • 表格的最后一个单元格 dp[4][8] = 10 表示在考虑前4个物品,背包容量为8时的最大价值为10。
  • 要找出具体选择的物品,可以回溯:
    • dp[4][8] = 10 来自 dp[3][3] + 6,所以选择了物品4(重量5,价值6)。
    • 然后看 dp[3][3] = 4,来自 dp[2][0] + 4,所以选择了物品2(重量3,价值4)。
    • dp[2][0] = 0,没有更多物品。
    • 因此,选择的物品是物品2和物品4,总重量=3+5=8,总价值=4+6=10。

验证

选择的物品:

  • 物品2:重量3,价值4
  • 物品4:重量5,价值6

总重量:3 + 5 = 8 ≤ 8(满足容量)
总价值:4 + 6 = 10

检查其他组合:

  • 物品1 + 物品3:重量2 + 4 = 6,价值3 + 5 = 8 < 10
  • 物品1 + 物品2 + 物品3:重量2 + 3 + 4 = 9 > 8(超重)
  • 物品3 + 物品4:重量4 + 5 = 9 > 8(超重)

确实,选择物品2和物品4能得到最大价值10。

状态转移表的可视化

为了更清晰地展示状态转移的过程,以下是 dp[i][w] 的填充过程:

  1. 初始化

    • 第一行(i=0)全部为0。
    • 第一列(w=0)全部为0。
  2. i=1 (物品1: weight=2, value=3)

    • w=2: max(dp[0][2], dp[0][0]+3) = max(0, 3) = 3
    • w=3: max(dp[0][3], dp[0][1]+3) = max(0, 3) = 3
    • … 直到w=8都是3
  3. i=2 (物品2: weight=3, value=4)

    • w=3: max(dp[1][3], dp[1][0]+4) = max(3, 4) = 4
    • w=4: max(dp[1][4], dp[1][1]+4) = max(3, 4) = 4
    • w=5: max(dp[1][5], dp[1][2]+4) = max(3, 7) = 7
    • … w=6,7,8: 类似
  4. i=3 (物品3: weight=4, value=5)

    • w=4: max(dp[2][4], dp[2][0]+5) = max(4, 5) = 5
    • w=5: max(dp[2][5], dp[2][1]+5) = max(7, 5) = 7
    • w=6: max(dp[2][6], dp[2][2]+5) = max(7, 8) = 8
    • w=7: max(dp[2][7], dp[2][3]+5) = max(7, 9) = 9
    • w=8: max(dp[2][8], dp[2][4]+5) = max(7, 9) = 9
  5. i=4 (物品4: weight=5, value=6)

    • w=5: max(dp[3][5], dp[3][0]+6) = max(7, 6) = 7
    • w=6: max(dp[3][6], dp[3][1]+6) = max(8, 6) = 8
    • w=7: max(dp[3][7], dp[3][2]+6) = max(9, 9) = 9
    • w=8: max(dp[3][8], dp[3][3]+6) = max(9, 10) = 10

可能的疑问与验证

在构建表格的过程中,可能会有以下疑问:

  1. 为什么在i=2, w=5时选择物品2?

    • 不选:dp[1][5] = 3
    • 选:dp[1][2] + 4 = 3 + 4 = 7
    • 选择物品2后,剩余容量为5-3=2,dp[1][2]表示在容量为2时前1个物品的最大价值是3(物品1)。
    • 因此总价值为物品2的价值4 + 物品1的价值3 = 7。
  2. 为什么在i=4, w=8时选择物品4?

    • 不选:dp[3][8] = 9
    • 选:dp[3][3] + 6 = 4 + 6 = 10
    • dp[3][3]表示在容量为3时前3个物品的最大价值是4(选择物品2)。
    • 因此总价值为物品4的价值6 + 物品2的价值4 = 10。

总结

通过构建这个状态转移表,我们可以清晰地看到每一步是如何基于之前的结果做出决策的。动态规划的核心在于利用子问题的解来构建更大问题的解,避免重复计算。在0-1背包问题中,状态转移表直观地展示了在不同物品选择和不同背包容量下的最优解。

最终的状态转移表

以下是完整的 dp[i][w] 表格:

i \ w012345678
0000000000
1003333333
2003447777
3003457899
40034578910

最优解

从表格中可以看出,dp[4][8] = 10 是最大价值。通过回溯,可以确定选择的物品是物品2和物品4。

补充说明

在实际编程实现时,可以优化空间复杂度,使用一维数组来替代二维数组,因为 dp[i][w] 只依赖于 dp[i-1][...]。但为了理解和教学目的,二维的状态转移表更为直观。

其他例子验证

为了确保理解正确,我再举一个简单的例子:

例子:

  • 物品1:重量=1,价值=1
  • 物品2:重量=2,价值=2
  • 物品3:重量=3,价值=3
  • 背包容量 W=4

状态转移表:

i \ w01234
000000
101111
201233
301234

最优解:dp[3][4] = 4,选择物品1和物品3(重量1+3=4,价值1+3=4)。

结论

通过构建和填充状态转移表,可以系统地解决0-1背包问题。关键在于:

  1. 正确定义状态 dp[i][w]
  2. 正确写出状态转移方程。
  3. 初始化边界条件。
  4. 按顺序填充表格,确保在计算 dp[i][w] 时所需的子问题已经解决。
  5. 通过回溯找出具体选择的物品。

这种方法不仅适用于0-1背包问题,也是理解和应用动态规划的通用框架。

http://www.xdnf.cn/news/4657.html

相关文章:

  • 前端实现文件下载
  • 案例分享 | 攻克ADAS开发测试难题,实现单元动态测试新突破
  • 力扣刷题Day 34:随机链表的复制(138)
  • MySQL大数据量查询优化
  • angular的cdk组件库
  • 苍穹外卖(订单状态定时处理、来单提醒和客户催单)
  • hadoop中的序列化和反序列化(4)
  • 快连LetsVPN安装指南
  • LeetCode20_有效的括号
  • 第2章 算法分析基础
  • 记录一下spring-cloud-starter-alibaba-nacos-config 2023.0.3.2与springboot版本及配置问题
  • 如何创建RDD
  • 【AI News | 20250507】每日AI进展
  • MySQL中为什么使用B+树结构、B+树和普通的平衡树的区别
  • Spark jdbc写入崖山等国产数据库失败问题
  • Linux/AndroidOS中进程间的通信线程间的同步 - 共享内存
  • AI 实践探索:辅助生成测试用例
  • 高性能轻量级Rust HTTP服务器框架Hyperlane:开启网络服务开发新体验
  • NLP核心技术解析:大模型与分词工具的协同工作原理
  • 排序算法——桶排序
  • 注意力机制(Attention)
  • 【关于ESP8266下载固件库的问题】
  • C++ 析构函数
  • 【Ollama】docker离线部署Ollama+deepseek
  • 从机器人到调度平台:超低延迟RTMP|RTSP播放器系统级部署之道
  • DeepSeek 入门:从注册到首轮对话全流程
  • Mysql如何完成数据的增删改查(详解从0到1)
  • 打造个人知识库,wsl+ollama部署deepseek与vscode集成
  • NetBox Docker 全功能部署方案(Ubuntu 22.04 + Docker)
  • k8s 中 deployment 管理的多个 pod 构成集群吗