背包问题基本解法 —— 《背包九讲》笔记

  相对于转载文章,我更喜欢写上一篇笔记,开篇给出原文链接。这样,能有些自己的东西,总结一番,对知识的理解能加深一层;别人看来,也更有价值。

  今天做USACO题目时,一道题不会,网上查到解法是01背包,于是重新看了《背包九讲》。相比第一次看,理解深的多,可见我还是在进步的,只要我没停下脚步。如果大家想看原文,那么只需要百度“背包九讲”就好了,百度文库中的“背包九讲 2.0”是正版,作者是崔添翼前辈,网上好像称他为dd大牛。这篇文章可以说是“背包问题”的权威了,如果我了解无误的话,背包问题的整套解法就是这篇文章总结出来的。在此,感谢崔添翼前辈的无私奉献!

  本篇笔记,暂时只对01背包、完全背包问题基本解法做出记录,今后如果用到更多,或许会回来更新。

------------------------------------------------------------------------------------------------------------------------------------------------

  背包问题模型:容量为V的背包,有N件物品,编号为i的物品,所占容量为Ci,所创造价值为Wi。

  01背包问题:每件物品只有一件,可以选择放或不放(即取0件或1件,故名01)。

  完全背包问题:每件物品有无穷件,只要装得下放多少都行(物品所取件数,所有值都可能取到,故名完全)。

  背包问题设问:

    1、最多能创造多少价值?

    2、背包放满时,最多(最少)能创造多少价值?

    3、背包放慢时,总共有多少种方案?

    ……

  这就是最基础的背包问题了,下面一一讲解。

------------------------------------------------------------------------------------------------------------------------------------------------

我们首先以01背包+问题1为例。

  对于背包问题,我们的基本思路是这样的:我们一件一件去放物品,从第1件开始直到第N件。设d[i][v]为放完第i件物品时,所占容量为v的情况下最大价值是多少。

  由于我们的问题1并不要求放满背包,那么显然有边界条件d[0][0~N]=0,这表示我们一件物品不放的时候,可以认为已经占了任意容量,创造的最大价值为0。

  而后,我们开始一件一件放物品,c++代码如下:

for(int i=1;i<=N;++i)
    for(int v=Ci;v<=V;++v) d[v][i]=max(d[v][i-1],d[v-Ci][i-1]+Wi);

  应该很容易理解,d[v][i]只可以由两种情况更新,一种是d[v][i-1],这表示第i件物品不放入背包(或者说放入0件);一种是d[v-Ci][i-1]+Wi,这表示第i件物品放入背包。我们用这两种情况的最大值去更新d[v][i],那么便考虑了所有情况,从而得到了d[v][i]得到了满足定义的值。

  这便是动态规划的思路了,很容易理解,也很好写。那么我们来分析一下它的复杂度:时间复杂度O(NV),空间复杂度O(NV)。

  下面我们来考虑优化。这种算法是考虑了所有情况,从而保证了正确性,因此时间复杂度上基本是无法优化的;而空间上,可以看到我们是层层递推上去的,求第i件物品的d[0~V][i]时,我们只需要用到d[0~V][i-1]而已,因此我们至少可以把空间缩减到两层,也就是O(2V)。而实际上,本题是可以让空间为一层的,即O(V)。

  这时,我们的初始化代码是:

for(int v=0;v<=V;++v) d[v]=0;

  而递推代码则如下:

for(int i=1;i<=N;++i)
    for(int v=V;v>=Ci;--v) d[v]=max(d[v],d[v-Ci]+Wi);

  其实并不难理解,我们把内层循环的顺序颠倒了。这样一来,很容易发现,我们内层循环至v时,d[v+1~V]实际上都是d[v+1~V][i],而d[0~v]则是d[0~v][i-1]。因此,党我们执行d[v]=max(d[v],d[v-Ci]+Wi)时,它实际上就相当于d[v][i]=max(d[v][i-1],d[v-Ci][i-1]+Wi),因为执行完这句,d[v]就相当于是d[v][i]了。

  如果上面的话你理解了,那么你也就明白了01背包问题的解法,时间O(NV)空间O(V)的解法。

那么我们讨论下01背包+问题2

  问题2很容易,我们要把背包放满,那么只需要修改一下初始化代码:

d[0]=0;
for(int v=1;v<=V;++v) d[v]=-∞;

  很好理解,一件物品不放时,只有d[0]有意义,其余容量都不可达到。因此,我们设为-∞,保证后面不会被max选到。而后面,只需要使用同样的递推代码,意义完全不变。这算法正确性太显然,我就不说明了。

接下来,我们来讨论完全背包问题

  完全背包中,每件物品可以放无穷件。如果我们考虑空间为O(NV)的算法,那么会相对复杂一点,这里不予讨论;而实际上,我们也只需要O(V)的空间,代码如下:

for(int i=1;i<=N;++i)
    for(int v=Ci;v<=V;++v) d[v]=max(d[v],d[v-Ci]+Wi);

  很简单,只需要把内层循环恢复正序即可。我这里不加解释,相信大家简单想想便可以理解。

  而对于问题1和问题2,和01背包一样,相对应修改初始化部分即可,代码完全一样。

那么,问题3呢?

  这个问题很容易,我也不打算给出代码。有兴趣的读者可以自己想想,我在USACO上所做那道题便是这种类型,想不出来的同学可以参考一下,这是博文链接

------------------------------------------------------------------------------------------------------------------------------------------------

  至此,本篇对背包问题的基础内容讲解完毕。如果大家仍有兴趣了解,不妨去看看《背包九讲》原文。

时间: 2024-08-07 00:42:17

背包问题基本解法 —— 《背包九讲》笔记的相关文章

背包问题九讲笔记_01背包(转)

http://blog.csdn.net/insistgogo/article/details/8579597 背包问题九讲笔记_01背包 分类: 算法导论2013-02-13 09:17 1752人阅读 评论(4) 收藏 举报 摘自Tianyi Cui童鞋的<背包问题九讲>,稍作修改,方便理解. 01背包问题描述 已知:有一个容量为V的背包和N件物品,第i件物品的重量是weight[i],收益是cost[i]. 限制:每种物品只有一件,可以选择放或者不放 问题:在不超过背包容量的情况下,最多

混合三种背包问题(背包九讲)

问题: 如果将P01.P02.P03混合起来.也就是说,有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包).应该怎么求解呢? 01背包与完全背包的混合: 考虑到在P01和P02中给出的伪代码只有一处不同,故如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可,复杂度是O(VN).伪代码如下: for i=1..N if 第i件物品属于01背包 for v

01背包问题的学习(来源:背包九讲)

问题: 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使价值总和最大. 思路: 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放.用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值.则其状态转移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}.这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的.所以有必要将它详细解释一下:"

有依赖的背包问题(背包九讲)

问题: 这种背包问题的物品间存在某种"依赖"的关系.也就是说,i依赖于j,表示若选物品i,则必须选物品j.为了简化起见,我们先设没有某个物品既依赖于别的物品,又被别的物品所依赖:另外,没有某件物品同时依赖多件物品. 算法: 这个问题由NOIP2006金明的预算方案一题扩展而来.遵从该题的提法,将不依赖于别的物品的物品称为"主件",依赖于某主件的物品称为"附件".由这个问题的简化条件可知所有的物品由若干主件和依赖于每个主件的一个附件集合组成.按照背

二维费用背包问题(背包九讲)

------------------------------------------ 前言: 对于一些背包问题,重点还是在于如何找出"背包容量"和"各种代价",以及价值,如此问题便迎刃而解了.下午 打篮球居然下冰雹了,悲催了.... ------------------------------------------ 问题: 二维费用的背包问题是指:对于每件物品,具有两种不同的费用:选择这件物品必须同时付出这两种代价:对于每种代价都有 一个可付出的最大值(背包容量)

多重背包问题(来源:背包九讲)

问题: 有N种物品和一个容量为V的背包.第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本算法: 这题目和全然背包问题非常类似.主要的方程仅仅需将全然背包问题的方程稍微一改就可以,由于对于第i种物品有n[i]+1种策略:取0件,取1件--取n[i]件.令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:f[i][v]=max{f[i-1][v-k*c[i]]+k*w

分组的背包问题(背包九讲)

在使用Ubuntu作为开发环境时经常需要在全局安装一些依赖框架等,这个时候就常常需要用到root权限,但是在Ubuntu下第一次使用su命令时会提示认证失败:查找资料后发现Ubuntu下root权限默认是锁定的,可能是处于安全考虑,但是作为开发人员肯定是需要root权限的. 在命令行中可以输入下面命令设置root密码,这样就能随时使用root权限了: [email protected]:~$ su 密码: su:认证失败 [email protected]:~$ sudo passwd [sud

背包问题问法的变化(背包九讲)

前言: 以上涉及的各种背包问题都是要求在背包容量(费用)的限制下求可以取到的最大价值,但背包问题还有很多种灵活的问法,在这里值得提一下.但是我认为,只要深入理解了求背包问题最大价值的方法,即使问法变化了,也是不难想出算法的.例如,求解最多可以放多少件物品或者最多可以装满多少背包的空间.这都可以根据具体问题利用前面的方程求出所有状态的值(f数组)之后得到.还有,如果要求的是"总价值最小""总件数最小",只需简单的将上面的状态转移方程中的max改成min即可.下面说一些

第二讲 完全背包问题(对背包九讲的学习)

学习自:背包九讲 题目 有N种物品和一个容量为V的背包,每种物品都有无限件可用.第i种物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路: 完全背包和01背包的区别是一个物品可以被拿无限次,我们之前01背包是拿或者不拿的max,比较,然后我们处理完全背包的时候每个物品拿多少次就好了 每个物品最优拿法=max(不拿,拿一个,拿两个,...,拿n个),抛出个问题,n难道是无限吗?显然不是,大前提是背包总空间或者说处理到第i个物