問題
給定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
其他相關參考《算法圖解》中的動態規劃”揹包問題“