常见的几种背包讲解

前几天听学长讲了背包,感觉有点难,于是慢慢琢磨,先是听学长分析,后来自己看崔添翼的背包问题九讲 2.0 beta 1.2 那个PDF,再结合网上的许多大牛写的背包讲解,还有就是题目的练习,终于有点体会了。

常见的几类背包问题

1、01背包

 题目 有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 Ci1,得到的 价值是 Wi。求解将哪些物品装入背包可使价值总和最大。

思路,动态规划

特点:每件东西只有一个,那就是说我们可以选放或不放。

用子问题定义状态:即F[i,v]由此我们从一个容量为v的背包可以获得最大价值。那么其状态转移方程就是

                  F[ i, v]=max{ F [ i-1,v] ,F [ i-1, v-Ci ]+Wi }

这个方程衍生出来的许多背包问题相关的方程,这个方程的意思就是说只考虑第 i 件物品的策略(放或不放),那么就可以转化为一个只和前 i−1 件物品相关 的问题。

1、如果不放第 i 件物品,那么问题就转化为“前 i−1 件物品放入容量为 v 的背 包中”,价值为 F[i−1,v]

2、如果放第 i 件物品,那么问题就转化为“前 i−1 件物品放 入剩下容量为 v−Ci 的背包中”,此时能获得的最大价值就是 F[i−1,v−Ci] 再加上 通过放入第 i 件物品获得的价值 Wi

伪代码如下:

F[0,0..V ] ← 0

   for i ← 1 to N

    for v ← Ci to V

      F[i,v] ←max{F[i−1,v],F[i−1,v−Ci] + Wi}

可以参考一份图表:来源:http://blog.csdn.net/mu399/article/details/7722810      

博主真是辛苦了

这张图很好的解释了dp过程是个什么样子的。ps:这图的顺序是从e开始装的,倒着装的,可能是作者书写的原因吧。

 

         

 

***************优化空间复杂度***********************

以上方法的时间和空间复杂度均为 O(V N),其中时间复杂度应该已经不能再优化 了,但空间复杂度却可以优化到 O(V )。

关键在于怎么将二维数组变成一维数组。我们可以发现我们的第二层循环是从Ci到V循环,那么我们也就发现对于第i件物品我们是不是可以从V往Ci递减过去,因为由状态转移方程我们知道对于第i件物品,它在V这点的最大价值并不和Ci到V之间的值会相互影响。只和第i-1件物品的选择有关

到这里,我们就知道可以去掉前面的一维空间了。这样就优化了空间复杂度。只是这样我们到最后也并不知道前i-1件物品用V的背包装最多装多大的价值。(根据题目的需要可以选择性优化空间)

伪代码:

F[0..V ]←0

   for i ← 1 to N

    for v ← V to Ci

      F[v] ←max{F[v],F[v−Ci] + Wi}

******************初始化的细节问题******************

上面的代码我们都初始化为0;那么是不是都是这样呢,很显然不是。

举个简单的例子,当我们求最小值的时候,除了dp[0]初始化为0.其他的都需要初始化 ∞ 。所以初始化则是需要根据题目的要求来选择。

 

2、多重背包问题

题目

有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 Mi 件可用,每件耗费的 空间是 Ci,价值是 Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超 过背包容量,且价值总和最大。

思路:

唯一与01背包问题不一样的地方就是同一件物品它有Mi件,那我们就想把它变成01背包来处理,那么,我们是不是可以将其看成取0件,取1件。。。。取m件。

那么我们就有状态转移方程

   F[i,v] = max{F[i−1,v−k∗Ci] + k∗Wi |0 ≤ k ≤ Mi}

复杂度是 O(V ΣMi)。

这么高的复杂度在做题的时候当然是不允许的,那么我们就需要降低复杂度,想一想我们是不是可以像其他的一些算法一样采用二进制的思想。

方法是:将第 i 种物品分成若干件 01 背包中的物品,其中每件物品有一个系 数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数分别为 1,2,2 ...2k−1,Mi −2k +1,且 k 是满足 Mi −2k +1 > 0 的最大整数。例如,如果 Mi 为 13,则相应的 k = 3,这种最多取 13 件的物品应被分成系数分别为 1,2,4,6 的四件 物品。

分成的这几件物品的系数和为 Mi,表明不可能取多于 Mi 件的第 i 种物品。

另外 这种方法也能保证对于 0...Mi 间的每一个整数均可以用若干个系数的和表示

这样就将第 i 种物品分成了 O(logMi) 种物品,将原问题转化为了复杂度为 O(V ΣlogMi) 的 01 背包问题,是很大的改进。

下面给出 O(logM) 时间处理一件多重背包中物品的过程:

 def MultiplePack(F,C,W,M)

if C ·M ≥ V

     CompletePack(F,C,W)

                      return

k ← 1

                 while k < M

ZeroOnePack(kC,kW)

M ←M −k

 k ← 2k

ZeroOnePack(C ·M,W ·M)

最好模拟一遍。

*********************可行性问题O(VN)的算法**************

这个算法是针对“每种有若干件的物品能否填满给定容量的背包“的问题

在这里就不扩充这个算法。如果想知道的话参考楼天成的“男人八题”的幻灯片

 

 

 

3、完全背包问题

题目 有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。放入第 i 种物品 的费用是 Ci,价值是 Wi。求解:将哪些物品装入背包,可使这些物品的耗费的费用总 和不超过背包容量,且价值总和最大。

思路:

类比多重背包:(因为重点不在这

状态转移方程

                                                     F[i,v] = max{F[i−1,v−kCi] + kWi |0 ≤ kCi ≤ v}

重点就是在于改算法的优化成O(VN  )的复杂度

这个算法使用一维数组,先看伪代码:

F[0..V ]←0

  for i ← 1 to N

    for v ← Ci to V

      F[v] ←max(F[v],F[v−Ci] + Wi)

重点来了,你很惊讶发现和01背包只有v的循环次序不同

为什么这个算法就可行呢?首先想想为什么 01 背包中要按照 v 递减的次序来循环。

让 v 递减是为了保证第 i 次循环中的状态 F[i,v] 是由状态 F[i−1,v −Ci] 递推而来

换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第 i 件物品”这件策 略时依据的是一个绝无已经选入第 i 件物品的子结果 F[i−1,v−Ci]

而现在完全背 包的特点恰是每种物品可选无限件,所以在考虑“加选一件第 i 种物品”这种策略时, 却正需要一个可能已选入第 i 种物品的子结果 F[i,v−Ci],所以就可以并且必须采用 v 递增的顺序循环。这就是这个简单的程序为何成立的道理。

这个算法也可以由另外的思路得出。例如,将基本思路中求解 F[i,v−Ci] 的状态转 移方程显式地写出来,代入原方程中,会发现该方程可以等价地变形成这种形式:

                                                                      F[i,v] = max(F[i−1,v],F[i,v−Ci] + Wi)!!!

没有像多重背包去乘它的系数

 

4、混合背包问题

当你理解了前面三种背包就不用看这个问题了,如果你混合背包不懂,返回去去看前面三种基本的背包问题吧

 

5、二维费用的背包问题

问题

二维费用的背包问题是指:对于每件物品,具有两种不同的费用,选择这件物品必 须同时付出这两种费用。对于每种费用都有一个可付出的最大值(背包容量)。问怎样 选择物品可以得到最大的价值。 设第 i 件物品所需的两种费用分别为 Ci 和 Di。两种费用可付出的最大值(也即两 种背包容量)分别为 V 和 U。物品的价值为 Wi。

思路:

算法 费用加了一维,只需状态也加一维即可。设 F[i,v,u] 表示前 i 件物品付出两种费用 分别为 v 和 u 时可获得的最大价值。状态转移方程就是:

F[i,v,u] = max{F[i−1,v,u],F[i−1,v−Ci,u−Di] +Wi}

如前述优化空间复杂度的方法,可以只使用二维的数组:当每件物品只可以取一次 时变量 v 和 u 采用逆序的循环,当物品有如完全背包问题时采用顺序的循环当物品 有如多重背包问题时拆分物品

***********************物品总个数的限制 ***************

有时,“二维费用”的条件是以这样一种隐含的方式给出的:最多只能取 U 件物品。 这事实上相当于每件物品多了一种“件数”的费用,每个物品的件数费用均为 1,可以 付出的最大件数费用为 U。换句话说,设 F[v,u] 表示付出费用 v、最多选 u 件时可得 到的最大价值,则根据物品的类型(01、完全、多重)用不同的方法循环更新,最后在 f[0...V,0...U] 范围内寻找答案!

 

 

**************************************背包九讲常见的就这5种背包问题(ps:剩下的四种学长说不常见,出现就会比较难了,O(∩_∩)O哈哈~)***********************

很多小伙伴会问不同题目的动态方程怎么折腾出来,这只能是刷题了,当你问这个问题的时候,肯定和我一样刚入门dp,题目做的多了,方程基本就回来,套路嘛~

 

资料来源,崔添翼的背包九讲+个人心得

ps:01背包的那张图我一直觉得很棒!谢谢博主了

 

posted @ 2017-07-21 00:03  ISGuXing  阅读(1759)  评论(0)    收藏  举报