zoukankan      html  css  js  c++  java
  • 常见的几种背包讲解

    前几天听学长讲了背包,感觉有点难,于是慢慢琢磨,先是听学长分析,后来自己看崔添翼的背包问题九讲 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背包的那张图我一直觉得很棒!谢谢博主了

  • 相关阅读:
    使用viewpager实现QQ界面切换效果
    实例教程十一:访问通信录中的联系人和添加联系人
    实例教程十四:视频刻录(调用前置摄像头)
    实例教程十二:为应用添加多个Activity与参数传递
    【30篇突击 android】源码统计八
    GIF动画 汇总帖子+实例
    Android ProgressBar 几乎全部的用法
    实例教程十三:拍照
    android NDK 知识汇总
    【原创】oracle中大数据量更新的测试
  • 原文地址:https://www.cnblogs.com/ISGuXing/p/7215470.html
Copyright © 2011-2022 走看看