01背包
背包最大重量为4。
物品为:
重量 价值
物品0 1 15
物品1 3 20
物品2 4 30
问背包能背的物品最大价值是多少?
碰到是否可以使用DP,我们需要思考是否存 在最优子结构,并且有重复子问题。通常情况下暴力解法就是你思考最优子结构的作用。
暴力解法:
我们在选择物品时有两种选择:选 或 不选,所以递归如图所示:
这样根节点的最优选项为:选0号物品 与 不选0号物品 中最优的那个。其他非叶子节点亦然。
而DP是自底向上进行的,当背包大小为1的时候,装不下2号物品;直到当背包大小为4的时候才可以装下2号物品。所以可以将问题中的物品与重量作为自变量,背包中物品的总价值作为因变量,这样DP数组就有意义了。
DP[i] [j],表示背包重量为j时,从物品中0-i号中任选物品时的价值。
状态转移方程根据选择 i 放入背包与不将 i 放入背包可得到:DP[i] [j] = max(DP[i-1] [j],DP[i-1] [j-w[i]]+v[i])
DP[i-1][j]: 不将当前节点i放入背包,那么就选择0-i-1中任意物品放入大小为j的背包中就是不选择当前节点的
DP[i-1][j-w[i]]: 选择当前物品i放入背包,那么背包大小已经占用了w[i]了,就将0-i-1中任意物品放入剩下的j-w[i]中。DP[i-1][j-w[i]]是最优子问题,加上当前选择i放入背包仍时候最优结构
初始化:当什么物品也不选时,不管重量是多少价值都为0。所以第0行初始化为0。当背包大小为0时,不管选择什么物品都装不下,价值为0。所以第0列初始化为0。
遍历顺序:递归自底向上是物品不断增多,重量不断加大的。所以从上到下,从左到右进行遍历。
终止条件:由于我们添加了一个无重量无价值的虚无物品作为初始什么都不选的条件(也就是添加了初始的一行)。所以终止条件为遍历到 i == 最后一个物品(len(weight)),j == 最大背包容量(len(max_weight))后终止
def bag_01(weight, value, max_weight):
length = len(weight)
dp = [[0 for i in range(max_weight + 1)] for i in range(length + 1)]
for i in range(1, length + 1):
for j in range(1, max_weight + 1):
# weight的下标0与dp数组的下标0含义不同,weight下标0的含义与dp下标1含义才相同,所以这里weight与value的下标得-1
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1] if j >= weight[i - 1] else 0)
print(dp)
return dp[length][max_weight]
bag_01([1,3,4], [15,20,30], 4)
[[0, 0, 0, 0, 0], [0, 15, 15, 15, 15], [0, 15, 15, 20, 35], [0, 15, 15, 20, 35]]
35