背包问题
基本背包问题
问题描述
有n件物品(序号为i),每件体积为v[i], 物品价值c[i],现有一个容积为m的背包
问这个背包能塞下的最大价值是多少
问题分析
这个问题是比较经典的动态规划问题,当n值较大时,穷举每件物品放不放入背包的情况是非常多的
构造方程f[i][j]
表示前i个物品+容器为j的背包能装下的最大价值
由此构建转移方程
f[i][j] = max(f[i-1][j], c[i] + f[i-1][j-[v[i]]])
理解这个方程:
假设我们已知f[x][y]
的所有值时(x<i y<=j)
,为此我们求值f[i][j]
我们可以将f[i][j]
视作f[i-1][j]
变化而来的,也就是我们在可选的物品里增加了一个物品
由此我们可以有两种选择
- 不将物品放入背包, 此种情况相当于没有添加心物品,此时的最大价值为f[i-1][j]
- 将该物品放入背包,此时背包里已有了
v[i]
体积,并且我们手中有了c[i]
的价值
而在此同时,我们还有一个容器还剩j-v[i]
的背包,此时背包剩余容积能装下的最大价值就是f[i-1][j-v[i]]
再将这个值加上我们最先放到背包里的新添加的物品的价值,就得出了此种情况下的最大价值f[i-1][j-v[i]+c[i]
在这个切入点除了这两种情况,还有别的没考虑的情况吗?没有
好,那么这两种情况各自的最大价值较大的那个就是前i件物品用容积为j的背包所能装下的最大价值,即f[i][j] = max(f[i-1][j], c[i] + f[i-1][j-[v[i]]])
小结
快滚去写代码
完全背包问题
问题描述
在基本背包问题的基础上,不限制每个物品的数量
问题分析
构造方程f[i][j]
表示前i个物品+容器为j的背包能装下的最大价值
仍然将f[i][j]
视作从f[i-1][j]
状态变化而来,即新添加了一个物品
沿用前面的判断
由此我们可以有两种选择
- 不将物品放入背包, 此种情况相当于没有添加心物品,此时的最大价值为f[i-1][j]
- 将(一件)该物品放入背包,此时背包里已有了`v[i]`体积,并且我们手中有了`c[i]`的价值
而在此同时,我们还有一个容器还剩`j-v[i]`的背包,此时背包剩余容积能装下的最大价值就是`f[i-1][j-v[i]]`
再将这个值加上我们最先放到背包里的新添加的物品的价值,就得出了此种情况下的最大价值`f[i-1][j-v[i]+c[i]`
在这个切入点除了这两种情况,还有别的没考虑的情况吗?
这次有,不但有,还很多
- 放入2件新添加的物品
- 放入3件新添加的物品
- 放入4件新添加的物品
- 放入5件新添加的物品
- 放入n件新添加的物品 (
当n*v[i] < j 背包放得下
)
之前我们求的是两种情况最大价值中的最大值,这次我们求(n+1)种情况最大价值的最大值
此外当我们放入第一件新增物品时,如果这时的最大价值小于不放的情况
这说明我们的这件物品价值很低了,没必要在尝试再放两件甚至更多,可以减少计算次数
转移方程
此时的转移方程
f[i][j] = max(
f[i-1][j],
1*c[i] + f[i-1][j-1*v[i]],
2*c[i] + f[i-1][j-2*v[i]],
3*c[i] + f[i-1][j-3*v[i]],
....
n*c[i] + f[i-1][j-n*v[i]],
)
小结
还不滚去写代码
多重背包
问题描述
在完全背包的基础上,每个物品不是1个也不是无穷多个
而是若干个,而且每个物品的数量可以不同
问题分析
你看懂了前面几个,这题做不出来,你都对不起我
在完全背包问题中,我们尝试将新添加物品从放入1个到放入n个
在多重背包问题中,我们将新添加物品从放入1个到放入x个(x为该物品数量)
转移方程
此时的转移方程
f[i][j] = max(
f[i-1][j],
1*c[i] + f[i-1][j-1*v[i]],
2*c[i] + f[i-1][j-2*v[i]],
3*c[i] + f[i-1][j-3*v[i]],
....
x*c[i] + f[i-1][j-x*v[i]],
)
小结
滚去写代码啊啊啊啊啊啊!!!
混合背包
问题描述
混合背包就是基本背包,完全背包,多重背包的缝合怪
每个背包的数量可以为1个,x个,无穷多个
问题分析
解每个f[i][j]
的时候,根据物品的数量选择就完事了,就是个if else的事情
小结
写啊写啊写代码
二维背包
问题描述
在前面问题的基础上,新引入另一个资源限制重量
w[i]表示每个物品的重量, 然后一个背包不但有容积x,还有重量限制y
问题分析
现在方程f[i][j]已经不满足我们的需求了,不过没关系,多加一维的问题,小事情
好了,新的方程为f[i][j][k] 表示容积为j,重量上限为k的背包装前i个物品的最大价值
状态转移方程
为避免将问题复杂话,咱们仅将二维背包和基本背包结合起来
f[i][j][k] = max(f[i-1][j][k], c[i]+f[i-1][j-v[i]][k-w[i]])
看懂了吗,还是那个模式,将新增物品放入还是不放入,放入一个还是多个
小结
你是已经是一个成熟的程序员里,应该不要让人催,自己就该赶紧去写代码
分组背包问题
问题描述
在基本背包问题的基础上将物品分为若干组,每组物品中仅能选择1个物品放入背包中
好了,我们这次又要改方程
f[g][i][j]
表示用容积为j的背包在前g组装前i个物品的最大价值
状态转移方程
f[g][i][j] = max(f[g-1][i][j], f[g-1][i-1][j-v[i]])
小结
懂?
华为机试练习题
分组背包+小变化
相当于将物品划分了若干组,每组物品个数为1~3个,其中1个为主物品,放该组内其他物品前必须将该物品放入背包,否则不能放
问题分析
还是沿用分组背包的方程
状态转移方程
# x, y, z表示第g组的各个物品的序号,第一个为主物品
x, y, z = fenzu[g]
f[g][i][j] = max(
f[g-1][i][j], # 第g组物品一个不放
w[x]+f[g-1][i][j-v[x]], # 只放主物品
w[x]+w[y] + f[g-1][i][j-v[x] - v[y]], # 放主物品+附件1
w[x]+w[z] + f[g-1][i][j-v[x] - v[z]], # 放主物品+附件2
w[x]+w[y]+w[z] + f[g-1][i][j-v[x]-v[y]-v[z]] # 放主物品+附件3
)