动态规划6-背包问题

有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,其他的重量都不存在,我们用负无穷来表示不可能,因为求的是最大价值嘛。
那么,我们整理一下我们的递推式子和初值:

那么我们求的值是什么呢? 回想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地是旧值,其余是新值。

核心伪代码:

1 f(0) = 0
2 f(1..m) = -∞
3 for i = 1 to n do
4    for j = m downto wi do
5        f(j) = max(f(j), f(j - wi))
6    endfor
7 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)而没有必要再一串数里取最大了。可见即使递推式相同,初值不同也会定义不同的函数,请不要忽略初值的作用啊。同样我们可以优化掉第一维。

核心伪代码:

1 初值f(0..m) = 0
2 for i = 1 to n do
3     for j = m downto wi do
4         f(j) = max(f(j), f(j - wi))
5     endfor
6 endfor

所求结果是f(m)

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

那结果是什么呢?
根据定义结果是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
 1 def array(n):
 2     a=[]
 3     for i in range(n):
 4         a.append(0)
 5     return a
 6 def max(x,y):
 7     if x>y:
 8         return x
 9     else:
10         return y
11
12 line=input().split()
13 n=int(line[0])
14 v=int(line[1])
15 w=[0]
16 p=[0]
17 f=array(v+1)
18 for i in range(n):
19     line=input().split()
20     w.append(int(line[0]))
21     p.append(int(line[1]))
22 for i in range(1,n+1):
23     for j in range(v,w[i]-1,-1):
24         f[j]=max(f[j],f[j-w[i]]+p[i])
25 print(f[v])        
时间: 2024-10-18 04:46:44

动态规划6-背包问题的相关文章

动态规划:背包问题

例题:装箱问题 ( http://www.wikioi.com/problem/1014/  ) 题目描述 有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数). 要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小. 输入描述 一个整数v,表示箱子容量,一个整数n,表示有n个物品 接下来n个整数,分别表示这n 个物品的各自体积 输出描述 一个整数,表示箱子剩余空间. 样例输入 24 6 8 3 12 7 9 7

算法导论三剑客之 动态规划 0-1背包问题

1 #include "iostream" 2 using namespace std; 3 4 float MAX(float m1,float m2){ 5 if(m1>=m2) 6 return m1; 7 else 8 return m2; 9 } 10 11 float bag_Zero_One(int n,float v,float p[],float w[]){ 12 if(n==0||v==0) 13 return 0; 14 else{ 15 float m2;

动态规划——0-1背包问题

 0-1背包问题: 描述:给定n中物品和一背包.物品i的重量是wi,其价值为vi,背包的容量为c,问应如何选择装入背包中的物品,使得装入背包中的物品总价值最大? 0-1背包问题是一个特殊的整数规划问题. 设所给0-1背包问题的子问题; 其最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,-,n时0-1背包问题的最优值.由0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式如下: NO1:递归实现 1 /* 2 *Description 递归实现 3 *设有一

动态规划01背包问题

动态规划0-1背包问题 ? 问题描写叙述: 给定n种物品和一背包.物品i的重量是wi,其价值为vi,背包的容量为C.问应怎样选择装入背包的物品,使得装 入背包中物品的总价值最大? ? 对于一种物品,要么装入背包,要么不装.所以对于一种物品的装入状态能够取0和1.我们设物品i的装入状态为xi,xi∈ (0,1),此问题称为0-11背包问题. 过程分析 数据:物品个数n=5,物品重量w[n]={0,2,2,6,5,4},物品价值V[n]={0,6,3,5,4,6}, (第0位,置为0,不參与计算,仅

动态规划之背包问题

背包问题是一个经典的算法问题,可以用动态规划,贪心法,分支界限法等方法解决.问题描述:有n个物品,编号1,2,3,..n,其中第 i 个物品重量为Wi 价值 Vi ,有一个容量为W的背包.在容量允许范围内,如何选择物品,可以得到最大的价值.(为了简单起见,假设物品的重量 Wi 和价值Vi 都是正数) 今天主要说的是0.1背包问题,解法是动态规划.当然,对于另外两种问题也会有所介绍. 问题分析: 用动态规划解问题首先要有效的找出子问题,可以通过这个子问题得推得原问题的解,通常子问题的实质是和原问题

动态规划初探 -- 背包问题

在为期一个星期的ACM集训之后,我就这样做了一个逃兵hhhh 在这一个星期里面,学长讲了快速排序,二分三分搜索,矩阵快速幂,线段树,BFS(广度优先搜索)和DFS(深度优先搜索),邻接表和哈希表,结构体和优先队列,背包问题和动态规划. 其中讲快速排序那天我还在考试,就没有去听,第二天找学长的时候也听得似懂非懂. 学长讲矩阵快速幂的时候爸妈来找我,也没有听.其中基本都学的不扎实. 所以都要后期重新再学一遍,巩固扎实.估计学长还会讲并查集和最小生成树,我就自己看看书吧~ ==============

【动态规划/多重背包问题】POJ1014-Dividing

多重背包问题的优化版来做,详见之前的动态规划读书笔记. dp[i][j]表示前i中数加得到j时第i种数最多剩余几个(不能加和得到i的情况下为-1)递推式为: dp[i][j]=mi(dp[i-1][j]≥0,即前i-1种数就能达到数字j) =-1(j<ai 或者 dp[i][j-ai]≤0,即再加上一个第i种数也无法达到j 或者 当前和小于当前数) =dp[i][j-ai]-1(可以达到的情况) #include<iostream> #include<cstdio> #inc

【算法数据结构Java实现】Java实现动态规划(背包问题)

1.背景 追随着buptwusuopu大神的脚步,最近在研习动态规划.动态规划应该叫一种解决问题的思想,记得又一次去某公司面试就被问到了这个. 多于动态规划的理解,大致是这样的,从空集合开始,每增加一个元素就求它的最优解,直到所有元素加进来,就得到了总的最优解. 比较典型的应用就是背包问题,有一个重量一定的包,有若干件物品,他们各自有不同的重量和价值,怎样背才能取得最大价值. 错误的理解:去价值/重量比最大的物品多装(之前我也是这么想的,但是在背包重量一定的情况下这么做并不合理,范例很容易想到)

动态规划0-1背包问题

最近看了一些简单的动态规划方面的例题 在学习的过程中发现 有的问题虽然不难 但是第一次看还是会有些问题 所以把自己弄0-1背包的问题拿出来给大家分享 不喜勿喷 网上资源特别多 讲解什么的就算了 其他人画的图都不错 递推关系: 设所给0-1背包问题的最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,-,n时0-1背包问题的最优值.由0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式: <!--[endif]--> 上式此时背包容量为j,可选择物品为i.此时

动态规划小结——背包问题

背包问题是动态规划的经典问题,所有初次学习动态规划都会讲述这个经典问题.因此,有必要弄清跟背包问题的所有分析过程并熟练掌握各种类型的代码. 一,完全背包问题 1.问题描述:有n种物品,每种物品有无限多个,第i个物品重量是wi,价值是vi,从这些物品中挑选总重量不超过W的物品,求出挑选物品价值的最大值. 限制条件:1≤n≤100,1≤wi,vi≤100,1≤W≤10000 2.解题思路:本题类似于"硬币问题",硬币问题只要求凑够相应的面值,这里只不过多了一个新的属性--价值,即不仅要凑够