最清楚的01背包問題講解


題目:

01背包問題描述:有編號分別為a,b,c,d,e的N=5件物品,它們的重量w分別是2,2,6,5,4,它們的價值v分別是6,3,5,4,6,每件物品數量只有一個,現在給你個承重為M=10的背包,如何讓背包里裝入的物品具有最大的價值總和sum_v?

 

在DP(dynamic programming,動態規划)問題中,01背包問題是比較基礎和簡單的了,但是網上很多人的講解要么長長一大段,長篇公式理論,要么就是知識把狀態轉移方程列了出來,而沒有說明為什么方程是這么寫的,下面我力圖將01背包問題中最簡單最核心的概念和思路講一下

1. 此01背包問題本質上是窮舉背包容量和可供選擇的物品(意思是里面的物品可能會放進背包,可能不會放進背包),取得最優解,只不過在窮舉的過程中,會根據狀態轉移方程,只計算可能獲得的最優解的部分,不去計算不是最優解的部分。具體來看,解題思路是把該問題分解為一個一個的小問題,一步步的通過小問題的最優解,最終得到大問題的最優解,跟我們人腦解題的思路是一樣的。比如第一個小問題是“當我的背包承重M=1,只有編號為a的物品可供選擇時,最優解是什么”,然后下一個小問題是建立在前一個小問題的基礎上“當我的背包承重M=1,有編號為a,b的物品可供選擇時,最優解是什么”,以此類推。

2. 為什么能列出狀態轉移方程?是因為每個狀態的最優解,都是根據之前的狀態的最優解獲得的。具體到背包問題,有以下幾點:

  a) 當物品備選情況(物品備選情況指:可供選擇的物品的集合)一致時,背包容量M越大,那么sum_v一定大於等於原來的值。

  b) 背包容量M確定時,可供選擇的物品N越多,那么sum_v一定大於等於原來的值。

  c) 由a)和b)可得,sum_v的最大值就是當M和N取到最大值時的sum_v

  c) 從思路上說,01背包問題有兩個維度:背包容量M,和供選擇物品數N。編程的本質是實現人類解決現實問題的思路。仔細想想,如果不借助計算機,你該如何解決這個問題?答案是,例如考慮M=1時,先考慮a能否放入背包,取得最大值,再考慮a和b能否放入背包(a和b都是備選,最終放入背包的可能是a,可能是b,也可能是ab),這時因此與之前只考慮a的情況相比,多了一個b,所以:

  • 要先判斷b能否單獨放進背包:
    •        如果不能,那么備選為a,b時最大值,等於備選只有a時的最大值(因為b是放不進背包的)。
    •   如果能,即b能夠放進去,還有兩種可能(即將b放進背包,和不將b放進背包),對這兩種可能性,要取最大值:
        •   最終將b放進去(注:此時物品a是否被放進背包是未知的,原因是:剩余的背包容量可能不足以放進物品a,即要在剩余可選物品里找出最優解。
        •   最終沒有將b放進去(因為后面可能有比b更合適的物品放進去),此時最大值等於備選只有a時的最大值

  用數學的方式描述上段話:sum_v[i][j]表示將前i件物品列為備選,背包容量為j時,能獲得的最大價值;w[i]表示第i件物品的重量,v[i]表示第i件物品的價值

#此時背包容量 M=1
if
1 >=w[2]: sum_v[2][1] = max(sum_v[1][j-w[2]] + v[2], sum_v[1][1]) else: sum_v[2][1] = sum_v[1][1]

  推廣到任意情況,即得到我們的狀態轉移方程

if j >=w[i]:
    sum_v[i][j] = max(sum_v[i-1][j-w[i]] + v[i], sum_v[i-1][j])
else:
    sum_v[i][j] = sum_v[i-1][j]

  sum_v的最大值就是sum_v[i][j]的最后一個元素

 

 

 

如何讀圖: 例如填充紅色格子這里,指在容量M=3,將a,b,c,d 這4件物品考慮在內時,可以取得的最大價值。

 

 

3. 計算時進行簡單的數據結構改造。因為當i=1時,即計算開始階段,還要考慮到如果第1件物品放不進去的情況,此時沒有物品在背包中,因此重量和價值都是0.因此需要在表示物品重量和價值的列表前加一個數據0。

另外,當沒有物品在背包中時,價值為0.所以需要sum_v[i][j]初始值全部設為0.

下面是詳細代碼:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import copy

class ZOPACK(object):
    def __init__(self,n,m,w,v):
        self.num = n
        self.capacity = m
        self.weight_list = [0,] + w
        self.value_list = [0,] + v
        self.Sum_Value_Metrix = self.__CreateMetrix__(self.num+1,self.capacity+1,0)
        
    def __CreateMetrix__(self,x,y,init_value):
        d2_list = []
        for i in range(x):
            d1_list = []
            for j in range(y):
                d1_list.append(init_value)
            d2_list.append(d1_list)
        return d2_list
        
    def dp(self):
        sum_v = self.Sum_Value_Metrix
        num = self.num
        capacity = self.capacity
        w = self.weight_list
        v = self.value_list
        for i in range(1,num+1):
            for j in range(1,capacity+1):
                if j >=w[i]:
                    #print("i,j:%s,%s" % (i,j))
                    sum_v[i][j] = max(sum_v[i-1][j-w[i]] + v[i], sum_v[i-1][j])
                else:
                    sum_v[i][j] = sum_v[i-1][j]
        print("The max value we can get is: ", sum_v[-1][-1])
        print(sum_v)

if __name__ == "__main__":
    num = 5
    capacity = 10
    weight_list = [2, 2, 6, 5, 4]
    value_list = [6, 3, 5, 4, 6]
    q = ZOPACK(num,capacity,weight_list,value_list)
    q.dp()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM