问题概述
0-1背包问题,其实就是给定若干个物体和一个可用于承物的背包。背包有一定的承重限制,而每个物体也有相应的重量和价值。“0-1”的意思就是物体只能选一次,核心问题在于如何在背包有限承重之下,使放入背包中的物体总价值最大。
代码实现
如果对这个问题还没有形成思路,安利下这个文章 0-1背包问题
from typing import List
def dp(weight: int, vals: List[int], weights: List[int]):
"""
@brief 0-1背包问题DP解法
@param weight 背包负重
@param vals 物品价值列表
@param weights 物品重量列表
@return 所选物品的最大总价值
"""
col = weight + 1
row = len(vals)
# 备忘录, 每行为考虑放入背包的物体。每列为背包的最大承重
mmo = [ col * [0] for i in range(row)]
# 第i行均为考虑过放第i个物品到背包中,行数累加,表示考虑的物品范围增加了。
# 如第1行表示,只考虑第1物品。第2行表示,同时考虑过第1、第2个物品。
# 第j列表示承重只有j的背包下的最优解。
# 只考虑第一个物品,只要能放入背包就拿走
for j in range(1, col):
if j >= weights[0]:
mmo[0][j] = vals[0]
# 考虑多个物品
for i in range(1, row):
# j 表示使用背包可承重。
# 由于承重为0时,天然是最优情况,故从1开始迭代
for j in range(1, col):
if j >= weights[i]:
# 既然,背包承重可以放下
# 那么此时有两种选择
# 1. j承重下,不放入当前物体
# 2. j承重下,放入当前物体,同时放入剩余空间最优方案选择方案
mmo[i][j] = max(
mmo[i-1][j],
vals[i] + mmo[i-1][j - weights[i]]
)
else:
# 单独放当前物体都放不下,只能放入先前的最优方案
mmo[i][j] = mmo[i-1][j]
return mmo[row - 1][col - 1]
ans = dp(4, [1500, 3000, 2000], [1, 4, 3])
后话
其实,在仔细观察后,可以发现只有当前计算行以及上一行会被使用到,其余的空间都是浪费的。对空间要求在苛刻一点,可以通过状态压缩只使用两行进行计算。