问题
给定N个物品和一个揹包。物品i的重量是Wi,其价值位Vi ,揹包的容量为C。问应该如何选择装入揹包的物品,使得放入揹包的物品的总价值为最大?
一、回溯法
分析
显然,放入揹包的物品,是N个物品的所有子集的其中之一。N个物品中每一个物品,都有选择、不选择两种状态。因此,只需要对每一个物品的这两种状态进行遍历。
解是一个长度固定的N元0,1数组。
套用回溯法子集树模板,做起来不要太爽!!!
代码
代码中有一个地方需要注意:
bags添加子集时,需要写bags.append(bag[:])而不是bags.append(bag),因为list中的bag赋值,属于引用。bag[:]属于复制。
n = 3 # 物品数量
c = 30 # 包的载重量
w = [20, 15, 15] # 物品重量
v = [45, 25, 25] # 物品价值
maxw = 0 # 合条件的能装载的最大重量
maxv = 0 # 合条件的能装载的最大价值
bag = [0, 0, 0] # 一个解(n元0-1数组)长度固定为n
bags = [] # 一组解
results=[]
bestbag = None # 最佳解
# 冲突检测
def conflict(k):
global bag, w, c
# bag内的前k个物品已超重,则冲突
if sum([y[0] for y in filter(lambda x: x[1] == 1, zip(w[:k + 1], bag[:k + 1]))]) > c:
return True
return False
# 套用子集树模板
def backpack(k): # 到达第k个物品
global bag, maxv, maxw, bestbag,bags,results
if k == n: # 超出最后一个物品,判断结果是否最优
cv = get_a_pack_value(bag)
cw = get_a_pack_weight(bag)
print("**************************")
print(bag)
print(bag[:])
print("***************************")
bags.append(bag[:])
results.append(bag)
print(bags)
print(results)
print("****************************")
if cv > maxv: # 价值大的优先
maxv = cv
bestbag = bag[:]
if cv == maxv and cw < maxw: # 价值相同,重量轻的优先
maxw = cw
bestbag = bag[:]
else:
for i in [1, 0]: # 遍历两种状态 [选取1, 不选取0]
bag[k] = i # 因为解的长度是固定的
if not conflict(k): # 剪枝
backpack(k + 1)
# 根据一个解bag,计算重量
def get_a_pack_weight(bag):
global w
return sum([y[0] for y in filter(lambda x: x[1] == 1, zip(w, bag))])
# 根据一个解bag,计算价值
def get_a_pack_value(bag):
global v
return sum([y[0] for y in filter(lambda x: x[1] == 1, zip(v, bag))])
# 测试
backpack(0)
print("bags",bags)
print("results",results)
print(bestbag, get_a_pack_value(bestbag))
打印如下:
**************************
[1, 0, 0]
[1, 0, 0]
***************************
[[1, 0, 0]]
[[1, 0, 0]]
****************************
**************************
[0, 1, 1]
[0, 1, 1]
***************************
[[1, 0, 0], [0, 1, 1]]
[[0, 1, 1], [0, 1, 1]]
****************************
**************************
[0, 1, 0]
[0, 1, 0]
***************************
[[1, 0, 0], [0, 1, 1], [0, 1, 0]]
[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
****************************
**************************
[0, 0, 1]
[0, 0, 1]
***************************
[[1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 0, 1]]
[[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1]]
****************************
**************************
[0, 0, 0]
[0, 0, 0]
***************************
[[1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 0, 1], [0, 0, 0]]
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
****************************
bags [[1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 0, 1], [0, 0, 0]]
results [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
[0, 1, 1] 50
注意到results中的子解,全部施一样的。并且同最后一个bag完全一直,是因为bag是引用。
二、动态规划
行是各种商品,列是不同容量的揹包
来看一个0-1揹包的例子:
weight=[2,2,6,5,4]
value=[3,6,5,4,6]
weight_most=10
有5个物体,考虑装入揹包,揹包的总承重是10。第一个物体重2,价值是3,如此类推。那么怎样才能在不超过揹包承重的范围下,使得揹包装的物体的总价值最高呢?
import numpy as np
weight=[2,2,6,5,4]
value=[3,6,5,4,6]
weight_most=10
def bag_0_1(weight,value,weight_most):#return max value
num = len(weight)
weight.insert(0,0)#前0件要用
value.insert(0,0)#前0件要用
bag=np.zeros((num+1,weight_most+1),dtype=np.int32)#下标从零开始
for i in range(1,num+1):
for j in range(1,weight_most+1):
if weight[i]<=j:
bag[i][j]=max(bag[i-1][j-weight[i]]+value[i],bag[i-1][j])
else:
bag[i][j]=bag[i-1][j]
# print(bag)
return bag[-1,-1]
result=bag_0_1(weight,value,weight_most)
print(result)
输出:
15
如果你想看bag这个矩阵也可以:
[[ 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 3 3 3 3 3 3 3 3 3]
[ 0 0 6 6 9 9 9 9 9 9 9]
[ 0 0 6 6 9 9 9 9 11 11 14]
[ 0 0 6 6 9 9 9 10 11 13 14]
[ 0 0 6 6 9 9 12 12 15 15 15]]
相关参考:
https://www.cnblogs.com/hhh5460/p/6920056.html
https://blog.csdn.net/your_answer/article/details/79274789
其他相关参考《算法图解》中的动态规划”揹包问题“