背包九讲

背包九讲

 发表于 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 小结

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值