回溯法 子集树模板 系列 ——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

 

其他相关参考《算法图解》中的动态规划”揹包问题“

 



 

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