背包九讲是动态规划思想的经典呈现,找了许久没有完整的python3实现,趁机总结一下。
1、0-1背包问题
二维DP数组解法:
# n, v分别代表物品数量,背包容积
n, v = map(int, input().split())
# w为物品价值,c为物品体积(花费)
w, cost = [0], [0]
for i in range(n):
cur_c, cur_w = map(int, input().split())
w.append(cur_w)
cost.append(cur_c)
#该初始化代表背包不一定要装满
dp = [[0 for j in range(v+1)] for i in range(n+1)]
for i in range(1, n+1):
for j in range(1, v+1): #可优化成 for j in range(cost[i], v+1):
if j < cost[i]:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-cost[i]]+w[i])
print(dp[n][v])
时间复杂度:O(VN),不能再优化,但对空间复杂度可以进行优化,使用一维数组。
一维DP数组解法:
# n, v分别代表物品数量,背包容积
n, v = map(int, input().split())
# w为物品价值,c为物品体积(花费)
w, cost = [0], [0]
for i in range(n):
cur_c, cur_w = map(int, input().split())
w.append(cur_w)
cost.append(cur_c)
#该初始化代表背包不一定要装满
dp = [0 for j in range(v+1)]
for i in range(1, n+1):
#注意:第二层循环要逆序循环
for j in range(v, 0, -1): #可优化成 for j in range(v, cost[i]-1, -1):
if j >= cost[i]:
dp[j] = max(dp[j], dp[j-cost[i]]+w[i])
print(dp[v])
值得注意的是:第二层循环中对背包容量 V 要逆序遍历,保证 dp[j-cost[i]] 是绝对不包含当前物品的选择。参考《背包九讲》
2、完全背包问题
一维DP数组解法:
只需要基于 0-1背包问题 的一维DP解法修改一行代码即可!!!
# n, v分别代表物品数量,背包容积
n, v = map(int, input().split())
# w为物品价值,c为物品体积(花费)
w, cost = [0], [0]
for i in range(n):
cur_c, cur_w = map(int, input().split())
w.append(cur_w)
cost.append(cur_c)
#该初始化代表背包不一定要装满
dp = [0 for j in range(v+1)]
for i in range(1, n+1):
#只需要将0-1背包一维DP解法中的二层循环改为顺序循环
for j in range(1, v+1):
if j >= cost[i]:
dp[j] = max(dp[j], dp[j-cost[i]]+w[i])
print(dp[v])
时间复杂度:O(VN)
为什么顺序遍历就能解决问题?
对于当前物品 i ,要么不拿为dp[j],要么拿为dp[j-cost[i]]+w[i],而dp[j-cost[i]]代表之前的状态中,也包含拿过物品 i 的状态,这样就包含了多次拿取物品 i 的情况。
优化:
3、多重背包问题
最直接的解法:
将每件物品的件数 Mi 作为独立的物品,这样时间复杂度为O(V sum(Mi))
# n, v分别代表物品数量,背包容积
n, v = map(int, input().split())
# w为物品价值,c为物品体积(花费)
w, cost, s = [0], [0], [0]
for i in range(n):
cur_c, cur_w,cur_s= map(int, input().split())
w += [cur_w]*cur_s
cost += [cur_c]*cur_s
n = len(w)-1
#该初始化代表背包不一定要装满
dp = [0 for j in range(v+1)]
for i in range(1, n+1):
for j in range(v, cost[i]-1, -1):
if j >= cost[i]:
dp[j] = max(dp[j], dp[j-cost[i]]+w[i])
print(dp[v])
优化:
class Solution:
# 0-1背包问题的写法
def max_value(self, n, m, v, w):
dp = [0] * (m + 1)
for i in range(1, n + 1):
for j in range(m, v[i] - 1, -1):
dp[j] = max(dp[j], dp[j - v[i]] + w[i])
return dp[-1]
if __name__ == '__main__':
import sys
n, m = map(int, input().split())
lines = sys.stdin.readlines()
v, w = [0], [0]
n = 0
for line in lines:
line = list(map(int, line.split()))
k = 1
while k <= line[2]: # 假设line[2]=13,k取1,2,4之后,line[2] = 6 < k = 8 退出循环
v.append(k * line[0])
w.append(k * line[1])
line[2] -= k
k *= 2
n += 1 # 物品总数加1
if line[2]:
v.append(line[2] * line[0])
w.append(line[2] * line[1])
n += 1
print(Solution().max_value(n, m, v, w))
作者:polaris
链接:https://www.acwing.com/solution/acwing/content/3988/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。