0/1背包問題:在能承受一定重量的背包中,放入重量不同,價值不同的幾件物品,怎樣放能讓背包中物品的價值最大?
比如,有三件物品重量w,價值v分別是
w=[5,3,2]
v=[9,7,8]
包的容量是5,也就是我們要求得
maxVal=v1+v2+v3……
約束條件為:ws=w1+w2+w3……
我們的思路是,列舉出所有可能的放入背包的選項,然后比較哪個價值大,這需要用到決策樹。
決策樹的思想是,用一組向量來描述當前的狀態,比如 [當前考慮的物品i, 當前背包的空間w, 當前已獲得的價值v],
決策樹左兒子表示不選取當前物品np,右兒子表示選取當前物品p,首先遞歸到索引為最后一個的物品,然后回溯,每回溯到一個物品時候就比較選取當前物品和不選取當前物品哪個更有價值
1 def maxVal(w, v, i, ws): 2 if i == 0: 3 if w[i] <= ws: 4 return v[i] 5 else: 6 return 0 7 without_i = maxVal(w, v, i-1, ws)#用遞歸算出不選取當前物品時候價值 8 if w[i] > ws: 9 return without_i 10 else: 11 with_i = maxVal(w, v, i-1, ws-w[i]) + v[i]#算出選取當前物品時候的價值 12 return max(without_i, with_i) 13 14 15 w = [5, 3, 2] 16 v = [9, 7, 8] 17 val = maxVal(w, v, 2, 5) 18 print(val)
這個方法可以正確運行,但是耗時為O(2^n),所以當數據量增大時候,耗時會急劇增大,有什么辦法可以減小耗時?同時列舉出所有可能得出結論?
這就用到動態規划了
拿上面這個例子來說
當我們考慮第二個也就是最后一個物品的時候,我們需要把第0個,第1個物品要不要選取考慮一次
當我們考慮第一個兒子時候,也要把第零個物品要不要選取考慮一次
。。。
當物品非常多的時候,就造成了非常大的浪費
那么,我們能不能每次考慮一個物品之后,就把每種情況下的特征和值記錄下來,以供以后考慮別的物品時候使用?
這就是動態規划
在這個背包問題中,我們可以使用(i,ws)來描述決策樹中每種情況,同時保存對應的值。
1 memo={} 2 def maxVal(w, v, i, ws): 3 try: 4 return memo[(i,ws)] 5 except KeyError: 6 if i == 0: 7 if w[i] <= ws: 8 memo[(i, ws)] = v[i] 9 return v[i] 10 else: 11 memo[(i, ws)] = 0 12 return 0 13 without_i = maxVal(w, v, i-1, ws) 14 if w[i] > ws: 15 memo[(i, ws)] = without_i 16 return without_i 17 else: 18 with_i = maxVal(w, v, i-1, ws-w[i]) + v[i] 19 res = max(without_i, with_i) 20 memo[(i, ws)] = res 21 return res 22 23 w = [5, 3, 2] 24 v = [9, 7, 8] 25 val = maxVal(w, v, 2, 5) 26 print(val)