0-1背包问题-(递归,记忆化搜索,动态规划)
程序员文章站
2022-05-12 15:53:07
...
现有n件物品和一个容量为c的背包。第i件物品的重量是重量为w[i],价值是v[i]。已知对于一件物品必须选择取(用1表示)或者不取(用0表示),且每件物品只能被取一次(这就是“0-1”的含义)。求放置哪些物品进背包,可使这些物品的重量总和不超过背包容量,且价值总和最大。
关键在于分析出状态转移方程:
(1)i=0 当j<w[0]时,m[0][j]=0;当j>=w[0]时,m[0][j]=v[0]。
(2)i>0 当j<w[i],m[i][j]=m[i-1][j];当j>=w[i],m[i][j]=max{m[i-1][j-w[i]]+v[i],m[i-1][j]}。
具体分析过程参考链接:https://www.nowcoder.com/discuss/3574
递归:
# 直接递归
def _bestvalue(w, v, index, c):
'''用[0, ...index]的物品填充容积为c的背包的最大价值'''
if c <= 0 or index < 0:
return 0
# 两种方式:
# 1.不放最后一件物品的最大价值
# 2.放入最后一件物品的最大价值
res = _bestvalue(w, v, index-1, c)
if c >= w[index]:
# 两种方式比较,取最大值
res = max(res, _bestvalue(w, v, index-1, c-w[index]) + v[index])
return res
def main(w, v, c):
n = len(w)
return _bestvalue(w, v, n-1, c)
w = [2,2,6,5,4]
v = [6,3,5,4,6]
c = 8
print(main(w, v, c))
记忆化搜索:
# 记忆化搜索
def _bestvalue(w, v, index, c):
'''用[0, ...index]的物品填充容积为c的背包的最大价值'''
if c <= 0 or index < 0:
return 0
if memory[index][c] != -1:
return memory[index][c]
# 两种方式:
# 1.不放最后一件物品的最大价值
# 2.放入最后一件物品的最大价值
res = _bestvalue(w, v, index-1, c)
if c >= w[index]:
# 两种方式比较,取最大值
res = max(res, _bestvalue(w, v, index-1, c-w[index]) + v[index])
memory[index][c] = res
return memory[index][c]
def main(w, v, c):
n = len(w)
return _bestvalue(w, v, n-1, c)
w = [2,2,6,5,4]
v = [6,3,5,4,6]
c = 8
memory = [[-1]*(c+1) for i in range(len(w)+1)]
print(main(w, v, c))
动态规划:
# 动态规划
def bestvalue(w, v, c):
if c <= 0:
return 0
dp = [[0]*(c+1) for i in range(len(w))]
# 先把第一行只放第1个物品的情况处理了
for i in range(0, c+1):
if i >= w[0]:
dp[0][i] = v[0]
for i in range(1, len(w)):
for j in range(0, c+1):
if j < w[i]:
# 剩余容量不足,i号物品不放入背包
dp[i][j] = dp[i-1][j]
else:
# 可以放入i号物品,计算状态转移方程
dp[i][j] = max(dp[i-1][j], v[i] + dp[i-1][j-w[i]])
# 计算选出的是哪些背包,1表示取,0表示不取
x = [0] * len(w)
for i in range(len(w)-1, 0, -1):
if dp[i][c] > dp[i-1][c]:
x[i] = 1
c -= w[i]
if c != 0:
if dp[0][c] != 0:
x[0] = 1
return dp[-1][-1], x
w = [2,2,6,5,4]
v = [6,3,5,4,6]
c = 8
print(bestvalue(w, v, c))