最清楚的01揹包問題講解

題目:python

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的物品可供選擇時,最優解是什麼」,以此類推。app

2. 爲何能列出狀態轉移方程?是由於每一個狀態的最優解,都是根據以前的狀態的最優解得到的。具體到揹包問題,有如下幾點:spa

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

  b) 揹包容量M肯定時,可供選擇的物品N越多,那麼sum_v必定大於等於原來的值。blog

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

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

  • 要先判斷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件物品的價值get

#此時揹包容量 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()
相關文章
相關標籤/搜索