背包九讲

背包九讲

 发表于 2017-09-15 |  分类于 算法

1 01背包问题

1.1 题目

背包九讲

1.2 基本思路

背包九讲

1.3 优化空间复杂度

背包九讲
背包九讲

1.4 初始化的细节问题

背包九讲

1.5 一个常数优化

背包九讲

1.6 小结

01背包问题是最基本的背包问题,它包含了背包问题中设计状态、方程的 最基本思想。另外,别的类型的背包问题往往也可以转换成01背包问题求解。 故一定要仔细体会上面基本思路的得出方法,状态转移方程的意义,以及空间复杂度怎样被优化。

2 完全背包问题

2.1 题目

背包九讲

2.2 基本思路

背包九讲

2.3 一个简单有效的优化

背包九讲

2.4 转化为01背包问题求解

背包九讲
背包九讲

2.5 O(VN)的算法

背包九讲

2.6 小结

完全背包问题也是一个相当基础的背包问题,它有两个状态转移方程。希 望读者能够对这两个状态转移方程都仔细地体会,不仅记住,也要弄明白它们 是怎么得出来的,最好能够自己想一种得到这些方程的方法。

事实上,对每一道动态规划题目都思考其方程的意义以及如何得来,是加 深对动态规划的理解、提高动态规划功力的好方法。

3 多重背包问题

3.1 题目

背包九讲

3.2 基本算法

背包九讲

3.3 转化为01背包问题

背包九讲

3.4 可行性问题O(V N )的算法

背包九讲

3.5 小结

背包九讲

4 混合三种背包问题

4.1 问题

如果将前面1、2、3中的三种背包问题混合起来。也就是说,有的物品只可 以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取 的次数有一个上限(多重背包)。应该怎么求解呢?

4.2 01背包与完全背包的混合

考虑到01背包和完全背包中给出的伪代码只有一处不同,故如果只有两类 物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个 物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可,复杂度 是O(V N )。伪代码如下:

背包九讲

4.3 再加上多重背包

背包九讲

4.4 小结

有人说,困难的题目都是由简单的题目叠加而来的。这句话是否公理暂且 存之不论,但它在本讲中已经得到了充分的体现。本来01背包、完全背包、多 重背包都不是什么难题,但将它们简单地组合起来以后就得到了这样一道一定 能吓倒不少人的题目。但只要基础扎实,领会三种基本背包问题的思想,就可 以做到把困难的题目拆分成简单的题目来解决。

5 二维费用的背包问题

5.1 问题

背包九讲

5.2 算法

背包九讲

5.3 物品总个数的限制

背包九讲

5.4 复整数域上的背包问题

另一种看待二维背包问题的思路是:将它看待成复整数域上的背包问题。 也就是说,背包的容量以及每件物品的费用都是一个复整数。而常见的一维背 包问题则是自然数域上的背包问题。所以说,一维背包的种种思想方法,往往 可以应用于二位背包问题的求解中,因为只是数域扩大了而已。

作为这种思想的练习,你可以尝试将后文中提到的“子集和问题”扩展到 二维,并试图用同样的复杂度解决。

5.5 小结

当发现由熟悉的动态规划题目变形得来的题目时,在原来的状态中加一维 以满足新的限制是一种比较通用的方法。希望你能从本讲中初步体会到这种方法。

6 分组的背包问题

6.1 问题

背包九讲

6.2 算法

背包九讲

背包九讲

6.3 小结

分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的 模型。不少背包问题的变形都可以转化为分组的背包问题(例如7),由分组的 背包问题进一步可定义“泛化物品”的概念,十分有利于解题。

7 有依赖的背包问题

7.1 简化的问题

这种背包问题的物品间存在某种“依赖”的关系。也就是说,物品i依赖于 物品j,表示若选物品i,则必须选物品j。为了简化起见,我们先设没有某个物 品既依赖于别的物品,又被别的物品所依赖;另外,没有某件物品同时依赖多件物品。

7.2 算法

背包九讲

7.3 较一般的问题

背包九讲

7.4 小结

NOIP2006的那道背包问题我做得很失败,写了上百行的代码,却一分未 得。后来我通过思考发现通过引入“物品组”和“依赖”的概念可以加深对这 题的理解,还可以解决它的推广问题。用物品组的思想考虑那题中极其特殊的 依赖关系:物品不能既作主件又作附件,每个主件最多有两个附件,可以发现 一个主件和它的两个附件等价于一个由四个物品组成的物品组,这便揭示了问 题的某种本质。

后来,我在《背包问题九讲》第一版中总结此事时说:“失败不是什么丢 人的事情,从失败中全无收获才是。”之后的NOIP2007的比赛中,我得了满 分。

8 泛化物品

8.1 定义

背包九讲
背包九讲

8.2 泛化物品的和

背包九讲

8.3 背包问题的泛化物品

一个背包问题中,可能会给出很多条件,包括每种物品的费用、价值等属 性,物品之间的分组、依赖等关系等。但肯定能将问题对应于某个泛化物品。 也就是说,给定了所有条件以后,就可以对每个非负整数v求得:若背包容量 为v,将物品装入背包可得到的最大价值是多少,这可以认为是定义在非负整 数集上的一件泛化物品。这个泛化物品——或者说问题所对应的一个定义域为 非负整数的函数——包含了关于问题本身的高度浓缩的信息。一般而言,求得 这个泛化物品的一个子定义域(例如0. . .V )的值之后,就可以根据这个函数的 取值得到背包问题的最终答案。

综上所述,一般而言,求解背包问题,即求解这个问题所对应的一个函 数,即该问题的泛化物品。而求解某个泛化物品的一种常用方法就是将它表示 为若干泛化物品的和然后求之。

9 背包问题问法的变化

以上涉及的各种背包问题都是要求在背包容量(费用)的限制下求可以取 到的最大价值,但背包问题还有很多种灵活的问法,在这里值得提一下。但是 我认为,只要深入理解了求背包问题最大价值的方法,即使问法变化了,也是 不难想出算法的。

例如,求解最多可以放多少件物品或者最多可以装满多少背包的空间。这 都可以根据具体问题利用前面的方程求出所有状态的值(F 数组)之后得到。

还有,如果要求的是“总价值最小”“总件数最小”,只需将状态转移方 程中的max改成min即可。

下面说一些变化更大的问法。

9.1 输出方案

背包九讲

9.2 输出字典序最小的最优方案

背包九讲

9.3 求方案总数

背包九讲

9.4 最优方案的总数

背包九讲

9.5 求次优解、第K 优解

背包九讲
背包九讲

9.6 小结

显然,这里不可能穷尽背包类动态规划问题所有的问法。甚至还存在一类 将背包类动态规划问题与其它领域(例如数论、图论)结合起来的问题,在这 篇论背包问题的专文中也不会论及。但只要深刻领会前述所有类别的背包问题 的思路和状态转移方程,遇到其它的变形问题,应该也不难想出算法。

触类旁通、举一反三,应该也是一个程序员应有的品质吧。