0-1背包
来源:互联网 发布:苹果手机短信备份软件 编辑:程序博客网 时间:2024/06/08 03:48
有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。可见这两种贪心法是不行的。如果你有别的贪心策略,也可以试试,万一成功了呢?
第一想法是?
(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,其他的重量都不存在,我们用负无穷来表示不可能,因为求的是最大价值嘛。
那么,我们整理一下我们的递推式子和初值:
f(i,j)=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪0(i=0∩j=0)−∞(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第一维显然只能
到n,第二维能到多大呢?最大也就是背包的容量,再大没意义,全是-∞了。那么我们的空尽复杂度是O(n * m),我们求这些值的时候,每个值最多只从之前求的两个值比较得到结果,所以时间复杂度也是O(n * m)。前面提到的枚举算法时间复杂度是O(2^n),虽然我们不能得出动态规划算法更快的结论,但通常认为m不太大。
令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,其他的重量都不存在,我们用负无穷来表示不可能,因为求的是最大价值嘛。
那么,我们整理一下我们的递推式子和初值:
那么我们求的值是什么呢? 回想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(0) = 0f(1..m) = -∞for i = 1 to n do for j = m downto wi do f(j) = max(f(j), f(j - wi)) endforendfor
所求结果是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(n,m)而没有必要再一串数里取最大了。可见即使递推式相同,初值不同也会定义不同的函数,请不要忽略初值的作用啊。同样我们可以优化掉第一维。
核心伪代码:
初值f(0..m) = 0for i = 1 to n do for j = m downto wi do f(j) = max(f(j), f(j - wi)) endforendfor
所求结果是f(m)
再换一种状态表示?刚才讲了,我们有重量和价值两个指标,那么我们令f(i,j)是决定了前i件物品,总价值恰好是j时的最小重量,那么经过类似的分析,我们可以写出这样的初值和递推式:
f(i,j)=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪0(i=0∩j=0)∞(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)
再换一种状态表示?刚才讲了,我们有重量和价值两个指标,那么我们令f(i,j)是决定了前i件物品,总价值恰好是j时的最小重量,那么经过类似的分析,我们可以写出这样的初值和递推式:
那结果是什么呢?
根据定义结果是max{x| f(n, x) <= m}, 同样我们可以优化掉一维的空间复杂度。那么时间复杂度是什么呢? O(n * sum(vi)) sum(vi)表示所有vi的和,也是第二维有意义的大小。
最终的结果也一样……还是要找到max{x| f(n, x) <= m}。
可见,动态规划问题状态表示十分灵活,不同的状态表示会有不同的解决方法。努力取发现吧!
根据定义结果是max{x| f(n, x) <= m}, 同样我们可以优化掉一维的空间复杂度。那么时间复杂度是什么呢? O(n * sum(vi)) sum(vi)表示所有vi的和,也是第二维有意义的大小。
同样我们也可以重新定义状态为f(i,j)是决定了前i件物品,总价值不超过j时的最小重量,则递推式不变,初值
f(0,1.. sum(vi))) = 0
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 62 53 84 9
输出示例
14
/*************************************************************************> File Name: 01背包.cpp> Author: Fan Deliang> Mail: fan0816fan@163.com> Created Time: 2016年05月05日 星期四 19时08分49秒 ************************************************************************/#include<iostream>#include<cstring>#include<string>#include<cstdio>#include<cmath>#include<algorithm>#include<stack>#include<map>#include<list>#include<set>#include<queue>#include<vector>#include<ctime>#define ll long long#define f(i,a,b) for(int i=a;i<=b;i++)#define m(a,b) memset(a,b,sizeof(a))#define MAX 0x3f3f3f3fconst ll MOD=1e9+7;using namespace std;ll dp[101][10010];int main(){ int n,W; cin>>n>>W; int w[101],p[101]; f(i,1,n)cin>>w[i]>>p[i]; m(dp,0); f(i,1,n){ f(j,1,W){ if(j-w[i]>=0) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+p[i]); else dp[i][j]=dp[i-1][j]; } } cout<<dp[n][W]<<endl; return 0;}
0 0
- 背包问题(0-1背包、完全背包、多重背包)详解
- 0-1背包----完全背包
- 0-1背包+分数背包
- 背包问题之0/1背包,完全背包,多重背包,混合背包
- 【背包问题】背包问题之0-1背包、完全背包、多重背包
- 0/1背包,完全背包,多重背包方程
- 0-1背包、部分背包和完全背包模板
- 背包问题(0/1背包,完全背包)
- 背包笔记-含0/1背包问题、完全背包问题、多重背包问题、二维背包问题、分组背包问题
- 0/1背包与完全背包
- 背包问题之0-1背包(一)
- 背包问题之0-1背包(二)
- 背包问题之0-1背包(三)
- 背包问题和0-1背包问题
- 背包问题start...:0-1背包
- 多重背包转换成0-1背包
- 贪心背包和0-1背包问题
- 0-1背包和部分背包问题
- Java注解-自定义注解
- ExtJS创建DataGrid
- HTML知识复习(1)
- Java基于jxl与模板导出excel并弹出下载
- Maven类包冲突终极解决小技若干
- 0-1背包
- 合并排序
- 对学生管理系统的心得
- CodeForces #589 D Boulevard 模拟
- redis del命令支持正则删除(pattern)
- 剑指offer(三十一)之孩子们的游戏(圆圈中最后剩下的数)
- 第十三周项目三 分数类中的运算符重载 (2)
- poj之旅——3181
- HDU 2151 worm