一、01背包
1.1题目
共N件物品,第i件件物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?
1.2 分析
01背包的dp数组含义是:dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
此时我们应该考虑下标为i的物品是否放入背包
(1)不放物品i:dp[i-1][j]
(2)放物品i:容量为 j 的背包需要为物品 i 预留出 i 的容量 wᵢ 这么大的空间才可以放物品 i,那么可以得知放入物品 i 之前的最大价值为dp[i-1][ j-wᵢ ]。然后再由 dp[i-1][ j-wᵢ ]+vᵢ 就是dp[i][j]放物品 i 的最大价值,其中,vᵢ 是物品 i 的价值。
然后取 Max( dp[i-1][j] , dp[i-1][ j-wᵢ ]+vᵢ ) 二者的最大值,即所求。
1.3 举例
有榴莲、菠萝、草莓共3件水果,重量分别为1kg,3kg,4kg,价值分别为 150元,200元,400元,背包容量为4kg,能够装入背包的东西的最大价值是多少?
这里:N=3,w[0]=1,w[1]=3,w[2]=4,v[0]=150,v[1]=200,v[0]=400,W=4
水果重量 | 背包容量 | 价值 | 背包容量为1kg | 背包容量为2kg | 背包容量为3kg | 背包容量为4kg |
榴莲重量 w[0]=1 | (1) | (2) | (3) | (4) |
菠萝重量 w[1]=3 | (5) | (6) | (7) | (8) |
草莓重量 w[2]=4 | (9) | (10) | (11) | (12) |
表格中的(1)-(12)就是我们要求解的背包能装下的东西的总价值,接下来逐一求解。
- 第一行我们可以理解为需要将榴莲装入背包中(现在只有榴莲可以放入背包,其他的水果都没有),首先第一个格子是1kg的背包,而榴莲正好也是1kg,那我们就可以把它装进包里,价值为150元,填入(1);第二个单元格是2kg的背包,也可以装下1kg的榴莲,价值为150元,填入(2);第三个单元格是3kg的背包,也可以装下1kg的榴莲,价值为150元,填入(3);第四个单元格是4kg的背包,也可以装下1kg的榴莲,价值为150元,填入(4)。
此时背包装的东西的最大价值是150元,这只是代表在只有榴莲的情况下的最大价值,也就是说只有榴莲的情况下,背包装的东西的最大价值是150元。
下面我们再加入菠萝,也就是说此时有榴莲、菠萝两种水果可以选择,现在我们继续计算:
- 第二行我们可以理解为需要将菠萝也装入背包中(现在有榴莲、菠萝两种水果可以放入背包,其他的水果都没有),首先第一个格子是1kg的背包,因为菠萝重量为3kg,所以此时装不下菠萝,只能装榴莲,那我们就可以把它装进包里,价值为150元,填入(5);第二个单元格是2kg的背包,同理,只能装榴莲,那我们就可以把它装进包里,价值为150元,填入(6);第三个单元格是3kg的背包,可以装菠萝,也可以装榴莲,但菠萝价值大于榴莲价值,所以3kg背包装菠萝,最大价值为200元,填入(7);第四个单元格是4kg的背包,可以同时装下榴莲和菠萝,其最大价值为350元,填入(8)。
此时背包装的东西的最大价值是350元,这只是代表在只有榴莲和菠萝的情况下的最大价值,也就是说只有榴莲和菠萝的情况下,背包装的东西的最大价值是350元。
最后我们再加入草莓,也就是说此时有榴莲、菠萝、草莓三种水果可以选择,现在我们继续计算:
- 第三行我们可以理解为需要将草莓也装入背包中(现在有榴莲、菠萝、草莓三种水果可以放入背包),首先第一个格子是1kg的背包,因为菠萝重量为3kg、草莓重量为4kg,所以此时装不下菠萝、草莓,只能装榴莲,那我们就可以把它装进包里,价值为150元,填入(9);第二个单元格是2kg的背包,同理,只能装榴莲,那我们就可以把它装进包里,价值为150元,填入(10);第三个单元格是3kg的背包,草莓重量为4kg,装不下,但是可以装菠萝,也可以装榴莲,但菠萝价值大于榴莲价值,所以3kg背包装菠萝,最大价值为200元,填入(11);第四个单元格是4kg背包,4kg背包有多种选择,一种是榴莲+菠萝组合,一种是草莓,其中,榴莲+菠萝价值为350元,草莓价值为400元,所以4kg背包最大价值为400元,只装草莓,填入(12)。
此时背包装的东西的最大价值是400元,这只是代表在有榴莲、菠萝和草莓的情况下的最大价值,也就是说在有榴莲、菠萝和草莓的情况下,背包装的东西的最大价值是400元。
经过计算和填数据,表格补充如下:
水果重量 | 背包容量 | 价值 | 背包容量为1kg | 背包容量为2kg | 背包容量为3kg | 背包容量为4kg |
榴莲重量 w[0]=1 | 150 | 150 | 150 | 150 |
菠萝重量 w[1]=3 | 150 | 150 | 200 | 350 |
草莓重量 w[2]=4 | 150 | 150 | 200 | 400 |
综上, 最终答案为装草莓,其最大价值为400元。
1.4 python代码实现
def knapsack(weight, value, bagSize):
n = len(weight)
"""创建一个n*bagSize+1的二维列表并全部初始化为0"""
dp = [[0] * (bagSize+1) for _ in range(n)]
#填充动态规划表格dp,其中dp[i][j]表示在前i个物品中,背包容量为j时的最大总价值
"""遍历物品列表的索引,n 是物品的数量"""
for i in range(n):
"""遍历背包容量,从1到背包容量 bagSize"""
for j in range(1, bagSize+1):
if weight[i] > j:
"""如果当前物品无法放入背包中,
则当前状态的价值等于上一个状态
的价值,即不选择当前物品,保持
背包容量不变。"""
dp[i][j] = dp[i-1][j]
else:
"""如果当前物品可以放入背包中,
则需要比较选择当前物品和不选择
当前物品的情况,取两者中的最大
值。"""
"""dp[i-1][j] 表示不选择当前物品,保持背包容量不变时的最大价值;
dp[i-1][j-weight[i]] + value[i] 表示选择当前物品,将当前
物品放入背包中后的最大价值——— 其中dp[i-1][j-weight[i]]表示
在放入当前物品之前的最大价值(容量为j的背包需要留出这个物品i的
容量weight[i]才可以放物品i),value[i]表示当前物品的价值。"""
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
# 返回最大值,就是最后那个格子的值
return dp[n-1][bagSize]
# 示例用法
weight = [1, 3, 4]
value = [150, 200, 400]
bagSize = 4
Max_price = knapsack(weight, value, bagSize)
print("背包中物品的最大价值为:", Max_price)