題目: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
用數學的方式描述上段話: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()