背包问题九讲 · 动态规划的思考艺术
一类被反复讲解近二十年的 dynamic programming 入门题——把若干带「费用 C」和「价值 W」的物品放入容量 V 的背包,求最大价值。九讲从最朴素的 01 背包(每件至多选一次)出发,逐步增加约束、变换问法:无限选、有上限、多类混合、双重费用、分组互斥、物品依赖……最后抽象成「物品即函数 h(v)」与「换聚合算子即换问法」两条主线。
每页都可调整容量 / 物品参数、单步执行,观察 DP 表逐格填出、读取的父节点与正在写入的格子高亮、代码逐行点亮,最后回溯出究竟选了哪几件落入背包。脉络一条线:状态 → 转移方程 → 循环方向 → 组合 / 求和 → 回溯。
基础三讲:状态、方程、循环方向
01 背包:每件至多选一次
全系列的地基。状态 F[i][v] =「前 i 件、容量 v 内的最大价值」,每件物品只有「选 / 不选」两个分支取 max。单步看二维表逐格填出、回溯把选中的物品落进背包;再看一维压缩为何要 v 逆序。
完全背包:每种可无限次选
和 01 背包只差一个循环方向:把一维的 v 从逆序改成顺序,读到的 F[v-C] 就已经含「本物品已选过」的结果,于是允许反复选。左右并排同步单步,看同一格在两种方向下读到的源值差在哪。
多重背包:每种限选 M 件
件数上限废掉了完全背包的本层递推特权。二进制拆分把件数约束编码进约 log M 件一次性物品,退化为 01 背包求解。
进阶四讲:混搭、升维、分组、依赖
混合背包:三类物品同台
同一份物品清单里三种类型混杂时,按类型分发到各自的 Pack 过程即可,不必也不该写一个统一的 Pack 函数。
二维费用:同时占两种资源
每件物品同时消耗两种费用(如重量 + 体积),容量也有两个上限 V、U。状态从 F[v] 升一维成 F[v][u],转移方程、边界、空间压缩的全套技术原样迁移——「升维即可」,体现状态设计的可扩展性。
分组背包:每组至多选一件
物品分成若干组,每组至多选一件。互斥既不靠加维也不靠分发,而靠三层循环的嵌套顺序:外层组 k、中层容量 v 逆序、内层组内物品 j。若将内外层写反便会同组多选——本页正确 / 错误两版并排单步,直观对比差异。
依赖背包:附件依附主件
附件只有在选了主件时才能选。两步化归:先对每个主件的附件集做 01 背包,把「主件 + 一组附件」打包成分组背包的候选,再跑分组背包。一般情形即树形 DP。
收束两讲:统一抽象与问法变化
泛化物品:物品即函数 h(v)
把物品抽象成函数 h(v),即分配费用 v 时能贡献的最大价值。背包问题归结为对所有 h 做一次取 max 的卷积,前七讲全是它的特例。
问法变化:换聚合算子即换问法
状态骨架不动,变的只是聚合算子与初始化语义:最大价值、最少件数、方案数与可行性四种问法在同一框架下统一。
动手体验 · 应用 demo
它真实跑在哪里
原版出处
- 《背包问题九讲》v2.0 · 崔添翼 (Tianyi Cui, dd_engi) github.com/tianyicui/pack 本系列是这份 2012 年中文 OI / ACM 经典 DP 教程的交互注解版;原文以 LaTeX 写成,CC BY-NC-SA 授权。九讲的脉络、递推方程与「物品即函数」「换聚合即换问法」两条主线都源自原作。
- 《背包问题九讲》v2.0 · 原版 PDF 本地 · v2.pdf 崔添翼原文的 LaTeX 排版 PDF(随本系列归档),九讲的完整定义、证明与代码均以此为准。
- 二进制拆分 / 背包基础 · 参考 PDF 本地 · binary_fund.pdf 多重背包二进制拆分等基础推导的补充材料(随本系列归档)。
-
Knapsack problem — Wikipedia
en.wikipedia.org
背包问题的标准定义、伪多项式时间复杂度
O(NV)、与复杂度的关系 (判定版 NP-complete、最优化版 NP-hard)。