部分背包问题是背包问题的一种变体,它与普通背包问题在物品的选择方面有所不同。在部分背包问题中,每个物品有一个数量限制,只能选择该物品的某一个数量,而不能选择该物品的部分数量。
问题描述
给定一组物品,每个物品有一个重量和一个价值,和一个整数表示该物品的数量。再给定一个容量为 C 的背包,如何选择物品放入背包中,使得这些物品的总重量不超过 C,且总价值最大?
解法思路
部分背包问题可以使用动态规划求解,可以从以下三个方面进行思考:
1. 状态定义
如同普通背包问题一样,考虑使用二维数组 dp[i][j]
表示前 i
个物品,对于背包容量为 j
,能够获得的最大价值。
2. 状态转移方程
对于第 i
个物品,在当前的背包容量为 j
时,需要考虑两种情况:
- 不选择第
i
个物品,则dp[i][j] = dp[i-1][j]
。 - 选择第
i
个物品,则dp[i][j] = dp[i-1][j-k*w[i]] + k*v[i]
,其中k
表示选择第i
个物品的数量,w[i]
和v[i]
分别表示第i
个物品的重量和价值。
因为在部分背包问题中,每个物品只能选择一定的数量,所以需要枚举这个数量,从而选择最优的方案。
综上,状态转移方程可以表示如下:
$$
dp[i][j] = \max{dp[i-1][j-kw[i]] + kv[i]}\ \ \ k\in[0,n_i]\ \ \ \ \ (1\le i\le N,0\le j\le C)
$$
3. 初始化
对于状态数组 dp[i][0]
,表示容量为 0 的背包,此时背包能够获得的最大价值为 0,即 dp[i][0] = 0
。
对于状态数组 dp[0][j]
,表示前 0 个物品的最大价值,此时背包能够获得的最大价值也都为 0,即 dp[0][j] = 0
。
初始化完毕后,从状态数组的右下角开始遍历,得到的最终结果即为能够获得的最大价值。
代码示例
下面给出一个使用 Python 语言实现部分背包问题的代码示例,其中 w
和 v
分别表示物品的重量和价值,c
表示背包的容量,n
表示物品的数量:
def fractional_knapsack(w,v,c,n):
dp = [[0] * (c+1) for _ in range(n+1)]
for i in range(1, n+1):
for j in range(1, c+1):
for k in range(min(w[i-1], j//w[i-1])+1):
dp[i][j] = max(dp[i][j], dp[i-1][j-k*w[i-1]] + k*v[i-1])
return dp[n][c]
下面给出一个样例,分别输入物品的数量、背包容量、物品的重量和价值,最终输出能够获得的最大价值:
>>> n = 3
>>> c = 50
>>> w = [10,20,30]
>>> v = [60,100,120]
>>> fractional_knapsack(w,v,c,n)
240
说明在背包容量为 50 时,选择第 2 和 第 3 个物品各 1 件,总重量为 50,总价值为 240,是能够获得的最大价值。