最近在複習演算法知識寫下這篇文章幫助自己理解記憶
01 揹包問題
01揹包問題的目標是在固定的容量限制內,達到最大的物品價值
01對含義:無法分割物品
01揹包問題通常有暴力回溯法和動態規劃兩種方式來解決
Brute Force
回溯法檢查所有組合 時間複雜度為指數級
很容易時間就爆表,這裡就不看了
動態規劃
動態規劃的核心在於尋找子問題,在這個題中我們首先去尋找子問題
尋找子問題
假設揹包最大重量為5
物品資訊如下
重量 | 價值 | |
---|---|---|
物品1 | 4 | 7 |
物品2 | 3 | 5 |
物品3 | 5 | 6 |
物品4 | 3 | 3 |
物品5 | 2 | 8 |
dp表的每一個位置儲存的是這麼大的揹包,最大收益是多少
我們首先考慮,如果只有物品1, 如果裝不下,收益就是0
如果我們的揹包可以裝下,那麼最大收益就是7,我們更新第一行
0 | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|
物品1 | 0 | 0 | 0 | 0 | 7 | 7 |
物品2 | ||||||
物品3 | ||||||
物品4 | ||||||
物品5 |
通過解決子問題獲得到全域性問題的解
現在考慮物品2,如果裝不下,收益就是上一行相同位置的結果
如果能裝下:判斷上一行相同位置的結果 此物品價值加上上一行去掉此物品重量後的重量最多能裝的價值
由此我們獲得狀態轉移方程
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+values[i])
0 | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|
物品1 | 0 | 0 (4 - weight of item 2) | 0 | 0 | 7 | 7 |
物品2 | 0 | 0 | 0 | 5 | max(7, (5 + 0)) | |
物品3 | ||||||
物品4 | ||||||
物品5 |
不斷迭代
0 | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|
物品1 | 0 | 0 | 0 | 0 | 7 | 7 |
物品2 | 0 | 0 | 0 | 5 | 7 | 7 |
物品3 | 0 | 0 | 0 | 5 | 7 | 7 |
物品4 | 0 | 0 | 0 | 5 | 7 | 7 |
物品5 | 0 | 0 | 8 | 8 | 8 | 13 |
最後結果為13
完整Python程式碼如下
#
# 程式碼中的類名、方法名、引數名已經指定,請勿修改,直接返回方法規定的值即可
#
# 計算01揹包問題的結果
# @param V int整型 揹包的體積
# @param n int整型 物品的個數
# @param vw int整型二維陣列 第一維度為n,第二維度為2的二維陣列,vw[i][0],vw[i][1]分別描述i+1個物品的vi,wi
# @return int整型
#
class Solution:
def knapsack(self, V: int, n: int, vw: List[List[int]]) -> int:
# write code here
dp = [[0] * (V + 1) for _ in range(n)]
for i in range(V + 1):
if i >= vw[0][0]:
dp[0][i] = vw[0][1]
for i in range(1, n):
for j in range(V + 1):
if j < vw[i][0]:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-vw[i][0]]+vw[i][1])
return dp[n-1][V]