背包问题
0-1背包
一共有n件物品,第i(i从1开始)件物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,能够装入背包的最大价值是多少?
问题描述
假如你要去野营,你有一个容量为6磅的背吧,需要觉得该携带下面的哪些东西。其中每样东西都有相应的价值,价值越大意味着越重要。
- 水(重3磅,价值10);
- 书(重1磅,价值3);
- 食物(重2磅,价值9);
- 夹克(重2磅,价值5);
- 相机(重1磅,价值6)。
请问携带哪些东西时价值最高?
解题思路
dp[i][j]表示只看前i个物品,总体积是j的情况下,总价值最大是多少
- 不选第i个物品:f[i][j]=f[i-1][j]
- 选第i个物品:f[i][j]=f[i-1][j-v(i)]+w[i],在两者之间选取大值。
初始化,f[0][0]=0,表示什么都没装,价值也为0.
计算得表格:
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
水 | 0 | 0 | 10 | 10 | 10 | 10 |
书 | 3 | 3 | 10 | 13 | 13 | 13 |
食物 | 3 | 9 | 12 | 13 | 19 | 22 |
夹克 | 3 | 9 | 12 | 14 | 19 | 22 |
相机 | 6 | 9 | 15 | 18 | 20 | 25 |
Python代码:
n, v = map(int,input().split())
goods = []
for i in range(n):
goods.append([int(i) for i in input().split()])
dp = [[0 for j in range(v+1)] for i in range(n)]
for i in range(n): # 物品ID
for j in range(1, v+1): # 表格中的纵轴,1,2,3,4,5,6
if goods[i][0]<=j:
dp[i][j] = max(goods[i][1]+dp[i-1][j-goods[i][0]], dp[i-1][j])
else:
dp[i][j] = dp[i-1][j]
print(dp[-1][-1])
输出:
25
优化
根据代码可以看出整个过程都只和上一层有关,和前面的无关,因此可以考虑将dp转化为只有一层的。但这里需要注意的是不能从前往后,会导致dp[j-goods[i][0]]变化,因此要倒序。
n, v = map(int, input().split())
goods = []
for i in range(n):
goods.append([int(i) for i in input().split()])
dp = [0 for i in range(v+1)]
for i in range(n):
for j in range(v, -1, -1):
if goods[i][