动态规划01背包问题_动态规划——经典背包问题(01背包、完全背包、多重背包)...

本文详细介绍了动态规划在解决背包问题中的应用,包括01背包问题的二维动态规划和一维动态优化,完全背包问题的一维动态规划及其优化,以及多重背包问题的处理策略,通过实例代码解析了每个问题的状态转移方程和优化技巧。
摘要由CSDN通过智能技术生成
f05552cf12f9f9a4acda928a54522272.gif点击蓝字关注我们

AI研习图书馆,发现不一样的精彩世界

数据
结构

1、0-1背包问题

描述:

有N件物品和一个容量为V的背包。

第i件物品的体积是vi,价值是wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包流量,且总价值最大。

3a64c7ec09fb0d54d29c419b41c8bee9.png

二维动态规划

f[i][j] 表示只看前i个物品,总体积是j的情况下,总价值最大是多少。result = max(f[n][0~V]) f[i][j]:

  • 不选第i个物品:f[i][j] = f[i-1][j];

  • 选第i个物品:f[i][j] = f[i-1][j-v[i]] + w[i](v[i]是第i个物品的体积) 两者之间取最大。初始化:f[0][0] = 0

  • (什么都不选的情况,不管容量是多少,都是0?)

代码如下:

n, v = map(int, input().split())
goods = []
for i in range(n):
goods.append([int(i) for i in input().split()])

# 初始化,先全部赋值为0,这样至少体积为0或者不选任何物品的时候是满足要求
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):
dp[i][j] = dp[i-1][j] # 第i个物品不选
if j>=goods[i-1][0]:# 判断背包容量是不是大于第i件物品的体积
# 在选和不选的情况中选出最大值
dp[i][j] = max(dp[i][j], dp[i-1][j-goods[i-1][0]]+goods[i-1][1])


print(dp[-1][-1])
时间复杂度:O(VN),不能再优化,但对空间复杂度可以进行优化,使用一维数组。
一维动态优化
从上面二维的情况来看,f[i] 只与f[i-1]相关,因此只用使用一个一维数组[0~v]来存储前一个状态。那么如何来实现呢?

第一个问题:状态转移

假设dp数组存储了上一个状态,那么应该有:

dp[i] = max(dp[i] , dp[i-v[i]]+w[i])

max函数里面的dp[i]代表的是上一个状态的值。

第二个问题:初始化

这里开始初始化一个长度为V+1一维数组,选取0个物品时,体积为0~V时的最大价值(值全部为0)。

第三个问题:递推关系

试想,要保证求取第i个状态时,用到一维数组中的值是第i-1个状态的。如果,从前往后推,那么当遍历到后面时,用到的状态就是第i个状态而不是第i-1个状态。比如:

dp[i] = max(dp[i] , dp[i-v[i]]+w[i])

这里的dp[i-v[i]]是已经重新赋值的,而不是上一个状态的值,所以这样是错误的。因此,要从后往前推

n, v = map(int, input().split())
goods = []
for i in range(n):
goods.append([int(i) for i in input().split()])

dp = [0 for i in range(v+1)]

for i in range(n):
for j in range(v,-1,-1): # 从后往前
if j >= goods[i][0]:
dp[j] = max(dp[j], dp[j-goods[i][0]] + goods[i][1])

print(dp[-1])
确定体积的情况:

如果要求的不是尽可能最大的价值,而是刚好等于背包容量的最大价值,那么该如何去做呢?

2、完全背包问题

描述:

有N件物品和一个容量为V的背包,每件物品都有无限个!

第i件物品的体积是vi,价值是wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包流量,且总价值最大。

cfac5fd2cd906052df9aff5dd0fbee02.png

一维动态规划

完全背包问题与01背包问题最大的区别:就是每一个物品可以选无数次,因此当我们考虑到第i个物品时,应该考虑的情况是:不选这个物品、选一次这个物品、选两次这个物品......直到不能再选(选的次数k,k*v[i] > j,j为当前背包容量),然后再从这些情况中选最大的。

代码如下:

n, v = map(int, input().split())
goods = []
for i in range(n):
goods.append([int(i) for i in input().split()])
dp = [0 for i in range(v+1)]
for i in range(n):
for j in range(v,-1,-1): # 从后往前
k = j//goods[i][0] # 能选多少次
# 从这些次里面取最大
dp[j] = max([dp[j- x* goods[i][0]] + x * goods[i][1] for x in range(k+1)])

print(dp[-1])
一维动态规划(优化)

上面的问题其实是01背包问题的延续,从后往前递推。但是对于这个问题,其实可以通过从前往后递推。如何理解呢?

假设在考虑第i个物品时的两个状态:

A:dp[k*v[i] + x]

B:dp[(k-1)*v[i] + x]

根据前面的归纳,从前一个状态递推:

  • 要求A的值,应该要从k+1个状态中选出最大的:

    dp[x] + k*w[i]
    dp[v[i] + x] + (k-1)*w[i]
    dp[2*v[i] + x] + (k-2)*w[i]
    ...
    ...
    dp[(k-1)*v[i] + x] + w[i]
    dp[k*v[i] + x
  • 要求B的值,应该要从k个状态中选出最大的:

    dp[x] + (k-1)*w[i]
    dp[v[i] + x] + (k-2)*w[i]
    dp[2*v[i] + x] + (k-3)*w[i]
    ...
    ...
    dp[(k-2)*v[i] + x] + w[i]
    dp[(k-1)*v[i] + x

可以看到,对应过来的话,这两个状态实际上只差一个w[i]的值。因此:

一方面可以根据前一个状态(i-1)推出此时的状态,另一方面由于当前状态前面的值也是当前问题的子问题,因此也可以从前面的值推到后面的值。

从前往后递推的代码如下:

n, v = map(int, input().split())
goods = []
for i in range(n):
goods.append([int(i) for i in input().split()])
dp = [0 for i in range(v+1)]
for i in range(n):
for j in range(v+1):
if j >= goods[i][0]:
dp[j] = max(dp[j], dp[j-goods[i][0]] + goods[i][1])

print(dp[-1])

3、多重背包问题

描述:

有N件物品和一个容量为V的背包。

第i件物品的体积是vi,价值是wi,数量是si。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包流量,且总价值最大。

46b07f095f802c3ddd97a58552526628.png

一维动态规划

与完全背包问题类似,只不过我们从后往前递推的时候,物体i选取的次数应该要重新考虑下:

k = min(s[i], j//v[i]), j为当前的背包容量

代码如下:

n,v = map(int, input().split())
goods = []
for i in range(n):
goods.append([int(i) for i in input().split()])

dp = [0 for i in range(v+1)]

for i in range(n):
for j in range(v, -1, -1):
# 考虑两种情况的最小值
k = min(j//goods[i][0], goods[i][2])
dp[j] = max([dp[j-x*goods[i][0]] + x*goods[i][1] for x in range(k+1)])

print(dp[-1])
一维动态规划(转换01背包)

想法很简单,直接把背包中的物品展开,展成很多数量为1的物品,这样就转换为01背包问题。代码如下:

n,v = map(int, input().split())
goods = []
for i in range(n):
goods.append([int(i) for i in input().split()])

new_goods = []

# 展开
for i in range(n):
for j in range(goods[i][2]):
new_goods.append(goods[i][0:2])

goods = new_goods
n = len(goods)

# 01背包问题
dp = [0 for i in range(v+1)]

for i in range(n):
for j in range(v,-1,-1):
if j>= goods[i][0]:
dp[j] = max(dp[j], dp[j - goods[i][0]] + goods[i][1])

print(dp[-1])

如果本文对你有所帮助,就点赞+在看吧~~

8c275fb22f9d9f5f35be2f3e2245b40f.pngBilibili : 洛必达数数CSDN博客:算法之美DLGitHub:statisticszhang 4695dd4f8f2a0f5159658a833ad32188.png

关注AI研习图书馆,发现不一样的精彩世界

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值