背包问题是一种经典的组合优化问题,通常表述为:给定一组物品,每种物品有一个重量和一个价值,以及一个容量有限的背包。目标是在不超过背包容量的前提下,选择物品放入背包,使得背包内物品的总价值最大。背包问题有许多变体,但核心思想都是如何在资源限制下做出最优决策。以下是一些常见的背包问题类型:
-
0/1 背包问题:物品不可分割,即要么完全放入背包,要么不放(取值为0或1)。
- 约束条件:背包总容量 W。
- 决策变量:对于第 i 个物品,xi∈{0,1},表示是否选择该物品放入背包。
- 目标函数:最大化背包内物品总价值 V=
,其中 vi 是第i 个物品的价值。
-
完全背包问题:
- 同样假设物品不可分割,但每种物品有无限数量可用。
- 约束条件:同上,背包总容量 W。
- 决策变量:对于第 i 个物品,可以取任意非负整数,表示放入背包的该物品数量。
- 目标函数:同上,最大化背包内物品总价值 V=
。
-
多重背包问题:
- 每种物品有固定的数量限制 bi,不能超过这个数量。
- 约束条件:背包总容量 W 和每种物品的数量上限 bi。
- 决策变量:对于第 i 个物品,0≤xi≤bi,表示放入背包的该物品数量。
- 目标函数:同上,最大化背包内物品总价值 V=
。
-
部分背包问题:
- 物品可以被部分放入背包,即可以只取部分数量,而非全取或不取。
- 约束条件:背包总容量 W。
- 决策变量:对于第 i 个物品,0≤xi≤wi,表示放入背包的该物品重量。
- 目标函数:同上,最大化背包内物品总价值 V=∑i=1nvixi,其中 wi 是第 i 个物品的重量。
解决背包问题常采用动态规划方法。以0/1背包问题为例,可以定义一个二维数组dp[i][w] 表示在前 i 个物品中,背包容量为 w 时能获得的最大价值。动态规划状态转移方程如下:
[ dp[i][w] = \max\left( \begin{array}{l} dp[i-1][w], \ dp[i-1][w-w_i] + v_i \end{array} \right) ]
解释:在物品 i 的决策时刻,可以选择不取该物品(保留前 i−1 个物品的最大价值),或者取该物品(牺牲 wi 容量,加上物品 i 的价值)。最终答案即 dp[n][W]。
针对不同类型的背包问题,动态规划的维度和状态转移方程会有所调整。此外,对于一些特殊结构的问题或大规模数据,可能需要使用贪心策略、回溯搜索、分支限界等其他算法来求解。
下面是一个使用动态规划解决0/1背包问题的Python示例:
Python
1def knapsack_01(weights, values, capacity):
2 n = len(weights)
3 dp = [[0 for _ in range(capacity + 1)] for _ in range(n + 1)]
4
5 for i in range(1, n + 1):
6 for w in range(1, capacity + 1):
7 if weights[i - 1] <= w:
8 dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1])
9 else:
10 dp[i][w] = dp[i - 1][w]
11
12 return dp[n][capacity]
13
14# 示例
15weights = [2, 3, 4, 5]
16values = [6, ½, 10, 16]
17capacity = 7
18
19max_value = knapsack_01(weights, values, capacity)
20print("最大价值:", max_value)
定义了一个名为knapsack_01
的函数,它接受物品的重量列表、价值列表以及背包容量作为参数,返回能装入背包的最大价值。在代码中,动态规划表 dp
的大小为 (n+1) x (capacity+1)
,其中 n
是物品数量,capacity
是背包容量。通过双重循环计算出每一项 dp[i][w]
的值,最后返回 dp[n][capacity]
即为所求的最大价值。
收起