leetcode-背包问题
问题描述
给你一个装载重量为 W 的背包和 N 个物品,每个物品有重量和价值两个属性。其中第 i 个物品的重量为 \(wt[i]\),价值为 \(val[i]\),现在让你用这个背包装物品,最多能装的价值是多少?
示例
N = 3, W = 4
wt = [2, 1, 3]
val = [4, 2, 3]
返回6,选择前两件物品,总重量小于W,可获得最大价值6.
动态规划
状态和选择
状态有两个,就是「背包的容量」和「可选择的物品」。
对于每件物品,你能选择什么?选择就是「装进背包」或者「不装进背包」
动态规划框架
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][... = 择优(选择1,选择2...)
明确dp含义
首先看看刚才找到的「状态」,有两个,也就是说我们需要一个二维 dp 数组。
dp[i][w]定义:对于前 i 个物品,当前背包的容量为 w,这种情况下可以装的最大价值是 dp[i][w]
比如说,如果 dp[3][5] = 6,其含义为:对于给定的一系列物品中,若只对前 3 个物品进行选择,当背包容量为 5 时,最多可以装下的价值为 6。
根据选择,状态转移
如果没有把这第 i 个物品装入背包,那么很显然,最大价值 \(dp[i][w] = dp[i-1][w]\),继承之前的结果。
如果把这第 i 个物品装入了背包,那么 \(dp[i][w] = dp[i-1][w - wt[i-1]] + val[i-1]\).
若装了第 i 个物品,就要寻求剩余重量 \(w-wt[i-1]\) 限制下的最大价值,加上第 i 个物品的价值 \(val[i-1]\)
代码实现
def backpack(W:int, N:int,wt: List[int], val: List[int]):
# dp[i][j]对于前i个物品,背包容量为j,所装的价值是dp[i][j]
dp = [[0]*(W+1) for _ in range(N+1)]
for i in range(1,N+1):
for j in range(1,W+1):
# 若不选择第i个物品
if j<wt[i-1]:
dp[i][j] = dp[i-1][j]
# 若选择第i个物品
else:
dp[i][j] = max(dp[i-1][j-wt[i-1]]+val[i-1],dp[i-1][j])
return dp[N][W]