python 完全背包问题_背包问题九讲python3实现

背包九讲是动态规划思想的经典呈现,找了许久没有完整的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

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值