回溯法 子集樹模板 系列 ——0-1揹包問題(回溯法和動態規劃及其比較)

問題

給定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

 

其他相關參考《算法圖解》中的動態規劃”揹包問題“

 



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章