一、算法概述
- 问题定义:分组背包问题是背包问题的经典变种,给定 n n n 组物品,每组包含若干个物品,每个物品有重量 w w w 和价值 v v v。每组中最多选择一个物品放入容量为 V V V 的背包,目标是在不超过背包容量的前提下,使背包内物品的总价值最大。
- 与其他背包问题的区别:相比01背包(每个物品独立选或不选)和多重背包(同一种物品有数量限制),分组背包强调"组"的概念,每组内物品互斥(最多选一个),是更复杂的组合决策问题,常用于资源分配、任务调度等场景。
二、算法思路
- 核心思想:通过动态规划方法,将问题分解为"考虑前 i i i 组物品,背包容量为 j j j 时的最大价值"这类子问题,利用子问题的解构建最终答案。
- 状态定义:定义二维数组
dp[i][j]表示考虑前 i i i 组物品,背包容量为 j j j 时能获得的最大价值。 - 状态转移方程:对于第
i
i
i 组物品,有两种选择:
- 不选第
i
i
i 组中的任何物品:
dp[i][j] = dp[i-1][j] - 选第
i
i
i 组中的第
k
k
k 个物品(若容量允许):
dp[i][j] = max(dp[i][j], dp[i-1][j-w[i][k]] + v[i][k])
最终状态转移方程为:dp[i][j] = max(dp[i-1][j], max{dp[i-1][j-w[i][k]] + v[i][k] | 第k个物品在第i组中且j≥w[i][k]})
- 不选第
i
i
i 组中的任何物品:
- 遍历顺序:先遍历物品组,再遍历背包容量,最后遍历组内物品,确保每组物品只选一个。
三、伪代码实现
1. 数据结构定义
结构体 Item:
cnt: 整数 # 该组物品数量
w[1..maxc]: 整数数组 # 重量数组
v[1..maxc]: 价值数组 # 价值数组
2. 分组背包主算法
算法:分组背包
输入:物品组数 n,背包容量 V,物品组数组 items[1..n],状态数组 dp[0..n][0..V]
输出:背包能装下物品的最大总价值
function KnapsackGroup(n, V, items[]):
// 初始化 dp 数组,容量为 0 时价值为 0,其余初始化为负无穷
for j from 1 to V:
dp[0][j] = -∞
dp[0][0] = 0
// 遍历每组物品
for i from 1 to n:
// 遍历背包容量
for j from 0 to V:
// 初始状态:不选第 i 组物品时的价值
dp[i][j] = dp[i-1][j]
// 遍历第 i 组中的每个物品
for k from 0 to items[i].cnt - 1:
if j >= items[i].w[k]:
// 尝试选择第 i 组的第 k 个物品
temp = dp[i-1][j - items[i].w[k]] + items[i].v[k]
// 更新最大价值
dp[i][j] = max(dp[i][j], temp)
// 找到容量 0 到 V 中的最大价值
maxValue = -∞
for j from 0 to V:
maxValue = max(maxValue, dp[n][j])
return maxValue
// 主程序
输入 n, V
for i from 1 to n:
输入 items[i].cnt
for j from 0 to items[i].cnt - 1:
输入 items[i].w[j], items[i].v[j]
输出 KnapsackGroup(n, V, items)
四、算法解释
1. 初始化阶段
- 将
dp[0][j](不考虑任何物品时)初始化为负无穷(容量大于0时无解),dp[0][0]初始化为0(容量为0时价值为0)。
2. 状态转移过程
- 遍历物品组:按顺序处理每组物品,确保前 i − 1 i-1 i−1 组的状态已计算完成。
- 遍历背包容量:从小到大或从大到小遍历容量(此处为从小到大,因为每组物品互斥,无需担心重复选择)。
- 遍历组内物品:对每组中的每个物品,若背包能容纳该物品,则计算选择该物品后的价值,并与不选该组物品的价值比较,取较大值。
3. 示例演示
- 假设背包容量
V
=
5
V = 5
V=5,有2组物品:
- 第1组(2个物品):物品1( w = 2 , v = 3 w=2, v=3 w=2,v=3)、物品2( w = 3 , v = 4 w=3, v=4 w=3,v=4)
- 第2组(1个物品):物品3( w = 4 , v = 5 w=4, v=5 w=4,v=5)
- 处理第1组:
- 容量
j
=
2
j=2
j=2 时,选物品1,
dp[1][2] = 3 - 容量
j
=
3
j=3
j=3 时,选物品2,
dp[1][3] = 4 - 容量
j
=
5
j=5
j=5 时,选物品1+物品2(总重量5),
dp[1][5] = 3+4=7
- 容量
j
=
2
j=2
j=2 时,选物品1,
- 处理第2组:
- 容量
j
=
4
j=4
j=4 时,选物品3,
dp[2][4] = 5 - 容量
j
=
5
j=5
j=5 时,比较不选第2组(7)和选物品3(
d
p
[
1
]
[
1
]
+
5
=
−
∞
+
5
=
−
∞
dp[1][1]+5= -∞+5=-∞
dp[1][1]+5=−∞+5=−∞),最终
dp[2][5]=7
- 容量
j
=
4
j=4
j=4 时,选物品3,
- 最终结果:遍历
dp[2][j]找到最大值7。
4. 优化说明
- 空间优化:若使用一维数组
dp[j],需从大到小遍历容量,避免同一组物品被重复选择。状态转移方程为:for i from 1 to n: for j from V down to 0: for k from 0 to items[i].cnt - 1: if j >= items[i].w[k]: dp[j] = max(dp[j], dp[j - items[i].w[k]] + items[i].v[k])
五、复杂度分析
- 时间复杂度:
- 算法包含三重循环:遍历物品组 O ( n ) O(n) O(n)、遍历背包容量 O ( V ) O(V) O(V)、遍历组内物品 O ( c m a x ) O(c_{max}) O(cmax)( c m a x c_{max} cmax 为每组物品的最大数量),因此总时间复杂度为 O ( n × V × c m a x ) O(n \times V \times c_{max}) O(n×V×cmax)。
- 空间复杂度:
- 使用二维数组
dp[i][j]时,空间复杂度为 O ( n × V ) O(n \times V) O(n×V);使用一维数组优化后,空间复杂度降为 O ( V ) O(V) O(V)。
- 使用二维数组
- 与其他背包问题的对比:
- 01背包: O ( n × V ) O(n \times V) O(n×V),每组1个物品
- 多重背包: O ( n × log c × V ) O(n \times \log c \times V) O(n×logc×V),每组同一种物品有数量限制
- 分组背包: O ( n × V × c m a x ) O(n \times V \times c_{max}) O(n×V×cmax),每组内物品互斥且类型不同
本文为作者(英雄哪里出来)在抖音的独家课程《英雄C++入门到精通》、《英雄C语言入门到精通》、《英雄Python入门到精通》三个课程的配套文字讲解,如需了解算法视频课程,请移步 作者本人 的抖音直播间。
4万+

被折叠的 条评论
为什么被折叠?



