題目描述:有編號分別爲1,2,3,4,5的五件物品,它們的重量分別是2,2,6,5,4,它們的價值分別是6,3,5,4,6,現在給你個承重爲10的揹包,如何讓揹包裏裝入的物品具有最大的價值總和?
揹包問題是典型的動態規劃問題,也有一定的規律可循,通常採用自底向上的方式,先解決小問題,並存儲,再解決大問題。
方法一:時間空間都爲O(n^2)的解法,但可以得到最大價值情況下拿了哪些東西
這裏dp[i][j]的含義是:在只有i個物品,最大容量爲j時,能獲得的最大價值
def bag(weight, value, max_W):
N = len(weight)
V = max_W
dp = [[0 for i in range(V+1)] for j in range(N+1)]
for i in range(1,N+1):
for j in range(1,V+1):
if weight[i-1]<=j:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1])
else:
dp[i][j] = dp[i-1][j]
for i in range(N+1):
print(dp[i][:])
return dp
def thing(weight,value,max_W,dp): # 找出具體選了那些物品,這個可以參考第一個鏈接
N = len(weight)
V = max_W
thing_flag = [0 for i in range(N)]
j = V
for i in range(N,0,-1):
if dp[i][j] != dp[i-1][j]:
thing_flag[i-1]=1
j -= weight[i-1]
print(thing_flag)
weight = [2,2,6,5,4]
value = [6,3,5,4,6]
max_W = 10
dp = bag(weight, value, max_W)
thing(weight,value,max_W,dp)
這個例子中對應的dp矩陣如下所示
可以通過從右下角向上回溯得到所有拿了的物品,見thing函數。
圖片參考自:https://blog.csdn.net/superzzx0920/article/details/72178544
方法二:時間爲O(n^2),空間爲O(2n)的解法
如果不需要求出具體選了哪些物品而只要最大價值,那在空間上還可以繼續優化,因爲我們求解dp[i]時,只用到了上一個子問題dp[i-1],所以存儲兩個一維數組即可。
def bag(weight, value, max_W):
N = len(weight)
V = max_W
dp = [0 for i in range(V)]
dp_next = [0 for i in range(V)]
for i in range(N):
for j in range(V):
if weight[i]<=j:
dp_next[j] = max(dp[j],dp[j-weight[i]]+value[i])
dp = dp_next.copy() #注意這裏要用.copy(),因爲不用就是淺複製
print(dp_next)
return dp
方法三:時間爲O(n^2),空間爲O(n)的解法
再進一步思考,計算dp[i][j]時只使用了dp[i-1][0……j],沒有使用dp[i-1][j+1]這樣的話,我們先計算 j 的循環時,讓j=V……1,即反方向計算,只使用一個一維數組即可。
def bag(weight, value, max_W):
N = len(weight)
V = max_W
dp = [0 for i in range(V)]
for i in range(N):
for j in range(V-1,-1,-1):
if weight[i]<=j:
dp[j] = max(dp[j],dp[j-weight[i]]+value[i])
print(dp)
return dp