感谢这些朋友们的文章,给了我很大启发:
https://blog.csdn.net/songyunli1111/article/details/94778914
https://blog.csdn.net/na_beginning/article/details/62884939
https://blog.csdn.net/qq_39445165/article/details/84334970
多重背包和01背包以及完全背包问题的区别在于:每个物品的数量是给定的,既不是01背包中的1个,也不是完全背包中的无穷多个。
其实这个和完全背包那个三重循环的解法很像,如果那个能理解,这个也不在话下了。
先上代码:
import numpy as np
def solution(max_weight,weight,value,num):
dp = np.zeros((len(weight)+1,max_weight+1),dtype=int)
for i in range(1,len(weight)+1):
for j in range(1,max_weight+1):
if weight[i-1] > j:
dp[i][j] = dp[i-1][j]
else:
count = min(num[i-1],j//weight[i-1])
dp[i][j] = dp[i-1][j]
for k in range(1,count+1):
temp = dp[i-1][j-k * weight[i-1]] + k * value[i-1]
if temp > dp[i][j]:
dp[i][j] = temp
print(dp)
return dp
数据:
weight = [1,2,2]
value = [6,10,20]
num = [10,5,2]
背包容量:8
分析程序:还是先创建一个二维数组dp,由于是多重背包,所以对于一个物品i(假设i=3,是3号物品),有如下选择:
所以在代码中,如果j大于当前物品单个的重量,就要算出最多能拿多少个
count = min(num[i-1],j//weight[i-1])
用min是因为如果背包容量太大,能装下物品地数量就大于给定数量了,因此用num[i-1]来做上界。
紧接着 dp[i][j] = dp[i-1][j] ,是考虑到了不选当前物品比选更优的情况。接着
for k in range(1,count+1):
temp = dp[i-1][j-k * weight[i-1]] + k * value[i-1]
if temp > dp[i][j]:
dp[i][j] = temp
对能拿的物品数量遍历,循环结束后会解出最优方案(是不拿,还是拿1个、2个…)
下面的代码能将选了什么物品输出:
def things(max_weight,dp,weight,value,num):
raw = len(weight)
col = max_weight
remain = dp[raw][col]
goods = [0,0,0]
while remain != 0:
if remain != dp[raw-1][col]:
count = min(num[raw-1],col//weight[raw-1])
for k in range(1,count+1):
if dp[raw][col] - k * value[raw-1] == dp[raw-1][col-k * weight[raw-1]]:
remain -= k * value[raw-1]
col -= k * weight[raw-1]
goods[raw-1] = k
raw -= 1
print(goods)
分析程序:if remain != dp[raw-1][col]这句,是看看拿没拿当前物品,如果左右相等说明没拿。如果不等,再往下看:
count = min(num[raw-1],col//weight[raw-1]) 是把能装多少个物品求出来。
if dp[raw][col] - k * value[raw-1] == dp[raw-1][col-k * weight[raw-1]] 这行的意思是,现在的背包状态拿出k个当前物品,剩余的价值 是否等于 前面的物品和除去这k个物品所占的重量的价值。若相等,说明就是拿了k个当前物品。
输出:
[4, 0, 2]
第一个物品拿4个,第三个物品拿2个。