【算法】【回溯篇】第7節:0-1揹包問題

本期任務:介紹算法中關於回溯思想的幾個經典問題

【算法】【回溯篇】第1節:八皇后問題

【算法】【回溯篇】第2節:解數獨問題

【算法】【回溯篇】第3節:正則表達式問題

【算法】【回溯篇】第4節:全排列問題

【算法】【回溯篇】第5節:組合問題

【算法】【回溯篇】第6節:子集問題

【算法】【回溯篇】第7節:0-1揹包問題


一、問題描述

給定n種物品和一揹包。物品i的重量是wi>0,其價值爲vi>0,揹包的容量爲c。
問應如何選擇裝入揹包中的物品,使得裝入揹包中物品的總價值最大? (要求使用回溯法)

輸入:
n, c = 4, 7
w = [3, 5, 2, 1]
v = [9, 10, 7, 4]

輸出:
20
[0, 2, 3]
    

二、算法思路

  • 0-1揹包問題是典型的“多階段決策最優解”問題:每個物品決策一次(拿或者不拿),共決策n次(n爲物品數量);最優解是揹包容量限制下的最大價值。

  • 本題使用回溯算法暴力窮舉所有可能的排列方式,並通過剪枝策略進行優化。

  • 暴力窮舉,每個物品都可能有2種(拿或者不拿),所有可能方式共有2n2^n,窮舉過程遵循深度優先搜索規則。

  • 維護一個長度爲n的數組res,用於保存每個物品的是否取。

  • 剪枝策略:揹包容量無法容納當前物品時,剪枝跳過。

  • 結算情形:所有物品遍歷完成時。

示例對應的遞歸樹如下,其中f(0,7,0)f(0,7,0)代表裝入編號爲0的物品前,揹包容量爲7,當前總價值爲0:
在這裏插入圖片描述


三、Python代碼實現

class Package01():
    def __init__(self, n, w, v):
        self.n = n  # 物品數量
        self.w = w  # 物品重量
        self.v = v  # 物品價值
        self.max_v = 0  # 記錄最大價值

        self.cur_ls = [False] * n  # 記錄當前物品清單
        self.res = []  # 記錄最大價值對應的物品清單

    def package01(self, cur_c, cur_v=0, index=0):
        if index == self.n:  # 當遍歷完所有物品進行結算!!!
            if self.max_v < cur_v:
                self.max_v = cur_v
                self.res = list(self.cur_ls)
            return
        if cur_c >= self.w[index]:  # 揹包裝的下當前物品時
            self.cur_ls[index] = True
            self.package01(cur_c - self.w[index], cur_v + self.v[index], index + 1)
        self.package01(cur_c, cur_v, index + 1)  # 不裝當前物品


def main():
    n, c = 4, 7
    w = [3, 5, 2, 1]
    v = [9, 10, 7, 4]

    pk = Package01(n, w, v)
    pk.package01(7, 0, 0)
    print(pk.max_v)
    print([i for i, _ in enumerate(pk.res) if _])


if __name__ == '__main__':
    main()

輸出結果:

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