0-1 背包问题(0-1 Knapsack Problem)
问题描述:给定 n 个物品,每个物品有重量 wi 和价值 vi,以及一个容量为 W 的背包。每个物品只能选择 放入或不放入 背包,求如何选择物品使得总价值最大且总重量不超过 W。
特点:每个物品只能选择一次。
应用场景:资源分配、投资组合选择等。
解决的问题:在资源有限(背包容量有限)的情况下,对具有不同价值和重量的物品进行选择,以达到价值最大化的决策问题。例如,在一次旅行中,旅行者的背包容量有限,需要从各种不同重量和价值的物品中选择携带哪些物品,以在不超过背包容量的前提下,使携带物品的总价值最高。
代码实现
def knapsack(V, costs, values):
n = len(costs)
# dp[v] 表示容量为v时的最大价值`在这里插入代码片`
dp = [0] * (V + 1)
for i in range(n):
# 逆序遍历容量,避免重复计算
for v in range(V, costs[i] - 1, -1):
# 选择当前物品或不选,取最大值
dp[v] = max(dp[v], dp[v - costs[i]] + values[i])
return dp[V]
使用示例
# 示例输入
V = 4 # 背包容量
costs = [3, 4, 1] # 物品费用
values = [20, 30, 15] # 物品价值
# 调用函数
max_value = knapsack(V, costs, values)
print("最大价值:", max_value) # 输出: 35(选择第1和3号物品,总费用3+1=4,价值20+15=35)
关键解释
-
动态规划状态定义
dp[v]
表示背包容量为v
时的最大价值。通过逐步考虑每个物品,更新不同容量下的最优解。 -
逆序遍历容量的原因
正序遍历会导致同一物品被多次选择(即变成完全背包问题),而逆序保证每个物品只被计算一次。 -
时间复杂度
( O(N \times V) ),其中 ( N ) 为物品数量,( V ) 为背包容量。
其他测试案例
案例 1
V = 5
costs = [2, 3, 4, 5]
values = [3, 4, 5, 6]
# 最大价值为7(选择物品0和1,费用2+3=5,价值3+4=7)
print(knapsack(V, costs, values)) # 输出: 7
案例 2
V = 10
costs = [5, 4, 6, 3]
values = [10, 40, 30, 50]
# 最大价值为90(选择物品1和3,费用4+3=7 ≤10,价值40+50=90)
print(knapsack(V, costs, values)) # 输出: 90
扩展:输出选择的物品列表
如果需要记录具体选择的物品,可以通过反向追踪 dp
数组实现:
def knapsack_with_items(V, costs, values):
n = len(costs)
dp = [0] * (V + 1)
# 记录每个容量下的物品选择路径
selected = [[] for _ in range(V + 1)]
for i in range(n):
for v in range(V, costs[i] - 1, -1):
if dp[v - costs[i]] + values[i] > dp[v]:
dp[v] = dp[v - costs[i]] + values[i]
selected[v] = selected[v - costs[i]] + [i] # 记录选择路径
return dp[V], selected[V]
# 示例
max_val, items = knapsack_with_items(4, [3,4,1], [20,30,15])
print("最大价值:", max_val) # 输出: 35
print("选择的物品索引:", items) # 输出: [0, 2]
通过这种方式,可以同时获取最大价值和具体选择的物品。