作为动态规划的基础,01背包的思想在许多动规问题中会经常出现,so,熟练的掌握01背包的思路是极其重要的;

  有n件物品,第i件物品(I = 1,2,3…n)的价值是vi, 重量是wi,我们有一个能承重为m的背包,我们选择一些物品放入背包,显然放入背包的总重量不超过m。我们要求选择物品的总价值最大,请问如何选择?这里我们假设所有出现的数都是正整数。

第一想法是? 
(1) 枚举?万能的枚举啊。但对于n件物品,每件都可以选择取或者不取,总的可能性有2n, n = 30就大约已经有10亿种可能了!枚举所有可能选择一种不超过背包承重并且价值最大的物品组合,枚举量太大了。
(2) 贪心? 嗯,有点意思。先选最贵重的物品?找个反例:
n = 3, m = 3
v = (2,2,3)
w = (1,2,3)
按照先选贵重物品的策略,会先选择价值为3的那个,并且背包装满了,但是如果我们选取前两个物品,总价值可以达到4。
可能你已经想到了,考虑“性价比”,先选取“性价比”高的。性价比怎么定义呢?用价值除以重量!先选取单位重量价值最大的物品试试?
再举个例子:
n = 3, m = 7
v = (2,3,4)
w = (3,4,5)
按我们的方法因为2/3 <  3/4 < 4 / 5,我们先选择第三件物品,但是选了它之后别的东西放不下了!总价值是4,但如果我们选择前两件物品可以拿到总价值5。可见这两种贪心法是不行的。如果你有别的贪心策略,也可以试试,万一成功了呢?
(3) 试试动态规划?

 
      我们从1-n一件一件选择物品,为什么要记录之前选择了哪些物品呢? 因为我们要计算重量和价值,那我们能不能只记录重量和价值呢?可以的。
      令f(i,j)表示选决定了前i件物品,重量恰好为j的时候能获得的最大价值。
假设我们已经知道了全部的f(i-1,*)的值,如何求f(i,*),换句话说我们有了状态表示还不够,如何求出递推式?
       对f(i,j)如果我们不选取第i件物品,则显然f(i, j) = f(i – 1,j)
如果我们要选取第i件物品,那么前i – 1件物品必须要达到j - wi且价值最大。由我们对f的定义,有f(i,j) = f(i-1,j - wi) + vi, 那么我们究竟选不选第i件物品呢?只好看哪个大了值大了,所以由

f(i,j) = max(f(i – 1, j) , f(i-1,j - wi) + vi)
当然只有j>= wi的时候我们才有选择第i件物品的权力。
那么初值是什么呢?f(0,*),一件物品也不选的时候,显然重量只能是0,其他的重量都不存在,我们用负无穷来表示不可能,因为求的是最大价值嘛。
那么,我们整理一下我们的递推式子和初值:
           {0(i=0∩j=0)
f(i,j)= {-∞(i=0∩j>0)
     {f(i-1,j)(i>0∪j<w)
     {max(f(i-1,j),f(i-1,j-wi)+vi)(i>0∩j>wi)
那么我们求的值是什么呢? 回想f的定义,最终的答案是背包要装的物品价值最大。那么答案应该是max{f[n][i]}  (0<=i<=n)  注意这里i可以等于0——如果背包一件物品都容纳不了呢?
至此我们的问题得到了解答。分析下复杂度,我们的f第一维显然只能
到n,第二维能到多大呢?最大也就是背包的容量,再大没意义,全是-∞了。那么我们的空尽复杂度是O(n * m),我们求这些值的时候,每个值最多只从之前求的两个值比较得到结果,所以时间复杂度也是O(n * m)。前面提到的枚举算法时间复杂度是O(2^n),虽然我们不能得出动态规划算法更快的结论,但通常认为m不太大。

回到老问题,我们如何得到具体选出哪些物品?惯用伎俩了,看f(i,j)到底是由f(i-1,j)得到的,还是由, f(i-1,j - wi) + vi得到的,从最终结果倒推回去,得到一个最优解。

优化?我们看一下这个递推式子核心就是f(i,j) = max(f(i – 1, j) , f(i-1,j - wi) + vi), 看一下f(i,*)只与f(i-1,*)相关,再仔细看看我们的第维j,只和更小的值相关,我们可以省掉一维i,然后倒着循环j,用旧的值更新新的值,这时f一部分是旧的值f(i-1,*),一部分是新的值f(i,*)。更具体地说小于j地是旧值,其余是新值。

核心伪代码:

f() =
f(..m) = -∞
for i = to n do
for j = m downto wi do
f(j) = max(f(j), f(j - wi))
endfor
endfor

所求结果是max{f(0..m)}

注意我们循环j只到wi,因为再小的j会导致我们无法选择第i件物品,这时我们直接使用不用第i件物品的旧值就好啦。简单吧?

那么现在,时间复杂度时不变的,空间复杂度降低O(m)了。

们尝试换一种状态表示? 我们令f(i,j)表示决定了前i件物品,总重量不超过j时能获得的最大价值。仔细想想递推式是不变的,那么初值呢?如果初值不变,f就没变化了……i= 0时,总重量时0,又因为0不超过任何整数,所以根据定义初值是f(0,*) = 0
那么最终结果呢?根据定义,最终结果是f(n,m)而没有必要再一串数里取最大了。可见即使递推式相同,初值不同也会定义不同的函数,请不要忽略初值的作用啊。同样我们可以优化掉第一维。

核心伪代码:

初值f(..m) =
for i = to n do
for j = m downto wi do
f(j) = max(f(j), f(j - wi))
endfor
endfor

所求结果是f(m)

再换一种状态表示?刚才讲了,我们有重量和价值两个指标,那么我们令f(i,j)是决定了前i件物品,总价值恰好是j时的最小重量,那么经过类似的分析,我们可以写出这样的初值和递推式:

{0(i=0∩j=0)
f(i,j)= {∞(i=0∩j>0)

{f(i−1,j)(i>0∪j<vi)

{max(f(i−1,j),f(i−1,j−wi)+vi)(i>0∪j>vi)

那结果是什么呢?
根据定义结果是max{x| f(n, x) <= m}, 同样我们可以优化掉一维的空间复杂度。那么时间复杂度是什么呢? O(n * sum(vi)) sum(vi)表示所有vi的和,也是第二维有意义的大小。

同样我们也可以重新定义状态为f(i,j)是决定了前i件物品,总价值不超过j时的最小重量,则递推式不变,初值
f(0,1.. sum(vi))) = 0
最终的结果也一样……还是要找到max{x| f(n, x) <= m}。
可见,动态规划问题状态表示十分灵活,不同的状态表示会有不同的解决方法。努力取发现吧!
例题
 
输入

第1行,2个整数,N和W中间用空格隔开。N为物品的数量,W为背包的容量。(1 <= N <= 100,1 <= W <= 10000)
第2 - N + 1行,每行2个整数,Wi和Pi,分别是物品的体积和物品的价值。(1 <= Wi, Pi <= 10000)
输出
 
输出可以容纳的最大价值。
 
输入示例

3 6
2 5
3 8
4 9
输出示例

14
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,w[],d[],m,f[];
int main()
{
cin>>n>>m;
for(int i=;i<=n;i++) cin>>w[i]>>d[i];
for(int i=;i<=n;i++)
for(int j=m;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+d[i]);
cout<<f[m];
}

如果对你有所帮助,别忘了加好评哦;么么哒!!下次见!88

- > 动规讲解基础讲解一——01背包(模板)的更多相关文章

  1. HDU 2602 - Bone Collector - [01背包模板题]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2602 Many years ago , in Teddy’s hometown there was a ...

  2. 01背包模板、全然背包 and 多重背包(模板)

    转载请注明出处:http://blog.csdn.net/u012860063 贴一个自觉得解说不错的链接:http://www.cppblog.com/tanky-woo/archive/2010/ ...

  3. 【洛谷P2722 USACO】 总分 01背包模板

    P2722 总分 Score Inflation 题目背景 学生在我们USACO的竞赛中的得分越多我们越高兴. 我们试着设计我们的竞赛以便人们能尽可能的多得分,这需要你的帮助 题目描述 我们可以从几个 ...

  4. P2871 [USACO07DEC]手链Charm Bracelet(01背包模板)

    题目传送门:P2871 [USACO07DEC]手链Charm Bracelet 题目描述 Bessie has gone to the mall's jewelry store and spies ...

  5. - > 动规讲解基础讲解三——混合背包(背包模板)

    将01背包,完全背包,和多重完全背包问题结合起来,那么就是混合三种背的问题 根据三种背包的思想,那么可以得到混合三种背包的问题可以这样子求解 for(int i=1; i<=N; ++i) if ...

  6. POJ 3624 Charm Bracelet(0-1背包模板)

    http://poj.org/problem?id=3624 题意:给出物品的重量和价值,在重量一定的情况下价值尽可能的大. 思路:经典0-1背包.直接套用模板. #include<iostre ...

  7. 解题报告:hdu2602 Bone collector 01背包模板

    2017-09-03 15:42:20 writer:pprp 01背包裸题,直接用一维阵列的做法就可以了 /* @theme: 01 背包问题 - 一维阵列 hdu 2602 @writer:ppr ...

  8. HDU 3339 In Action【最短路+01背包模板/主要是建模看谁是容量、价值】

     Since 1945, when the first nuclear bomb was exploded by the Manhattan Project team in the US, the n ...

  9. 洛谷 P2871 [USACO07DEC]手链Charm Bracelet && 01背包模板

    题目传送门 解题思路: 一维解01背包,突然发现博客里没有01背包的板子,补上 AC代码: #include<cstdio> #include<iostream> using ...

随机推荐

  1. [ZJOI2005]沼泽鳄鱼

    题目描述 潘塔纳尔沼泽地号称世界上最大的一块湿地,它地位于巴西中部马托格罗索州的南部地区.每当雨季来临,这里碧波荡漾.生机盎然,引来不少游客. 为了让游玩更有情趣,人们在池塘的中央建设了几座石墩和石桥 ...

  2. [ZJOI2006]Book书架

    Description Sally有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号.Sally在看书的时候,每次取出一本书,看完后放回书柜 ...

  3. 倒计时和div幻灯片

    倒计时从10秒开始,10秒之后,同意按钮可以使用. 设置一个区域背景,三张照片滚动显示,左右按钮可以下一张上一张,如果点击了左右按钮就不再自动滚动.

  4. [ HAOI 2008 ] 圆上的整点

    \(\\\) Description 给出一个整数 \(r\) ,求圆 \(x^2+y^2=r^2\) 上的整点数. \(r\le 2\times 10^9\) \(\\\) Solution 神题. ...

  5. ubuntu下删除和新建用户(并有su权限)

    http://blog.csdn.net/speedme/article/details/8206144ubuntu下删除和新建用户(并有su权限) 如何创建ubuntu新用户?输入:sudo add ...

  6. Java设置全局热键——第三方包jintellitype实现

    Java原生API并不支持为应用程序设置全局热键.要实现全局热键,需要用JNI方式实现,这就涉及到编写C/C++代码,这对于大多数不熟悉C/C++的javaer来说,有点困难.不过幸好,国外有人已经实 ...

  7. JAVA基础——集合浅析

    Java  集合      数组是一种很常见的数据结构,开始接触编程的时候多数程序都和数组相关.刚开始接触Java时也是一直使用数组写一些程序,后来越来越觉得数组这东西没法满足需求了,这时一位“前辈” ...

  8. linux常用操作记录

    vim:多行注释 vim中多行注释和多行删除命令,这些命令也是经常用到的一些小技巧,可以大大提高工作效率.   多行注释:   1. 首先按esc进入命令行模式下,按下Ctrl + v,进入列(也叫区 ...

  9. Linux配置网卡、网卡会话、网卡bonding

    配置网卡  1.路径:  /etc/sysconfig/network-scripts/ifcfg-eno16777728 2.含义:HWADDR=00:0C:29:9C:D6:4D   Mac地址 ...

  10. oc门的设计思路

    oc门,又称集电极开路门,Open Collector. mc1413bd就是这样一款oc门,在网上听说又叫uln2003a. 1.其符号是: 2.内部的组成就是NPN型的达林顿管 用非门的意思,我的 ...