当前位置: 首页 > news >正文

背包DP之分组背包

背包DP之分组背包

    • 一、分组背包基础认知
      • 1.1 问题定义
      • 1.2 核心特征
    • 二、分组背包的状态设计与递推
      • 2.1 状态定义
      • 2.2 递推关系
      • 2.3 遍历顺序
    • 三、代码实现
      • 3.1 基础二维DP实现
      • 3.2 空间优化:一维DP实现
        • 优化说明:
    • 四、分组背包的变种与应用
      • 4.1 变种问题
      • 4.2 应用场景
    • 五、时间复杂度与优化
      • 5.1 时间复杂度
      • 5.2 优化技巧

分组背包在基础背包的基础上增加了“分组”约束——物品被划分为若干组,每组中最多选择一个物品,这种模型广泛应用于“互斥选择”场景(如“从多个套餐中选一个”“从多类工具中选一种”)。

一、分组背包基础认知

1.1 问题定义

给定m组物品,每组物品包含若干个物品;每个物品有重量w[i][k]和价值v[i][k]i为组号,k为组内物品序号);背包容量为C。要求:

  • 每组最多选择一个物品(可以不选)。
  • 在总重量不超过C的前提下,最大化选择物品的总价值。

示例:

  • 输入:m=2组物品,C=5
    • 第1组:[(w=2, v=3), (w=3, v=5)]
    • 第2组:[(w=1, v=2), (w=4, v=6)]
  • 输出:8(选择第1组的(3,5)和第2组的(1,2),总重量3+1=4≤5,总价值5+2=7?不,最优为第1组(2,3)和第2组(4,6),总重量2+4=6>5;正确最优:第1组(3,5)和第2组(1,2)总价值7,或第2组(4,6)和第1组不选,总价值6 → 实际最优为7)

1.2 核心特征

  • 分组约束:物品按组划分,组内物品互斥(选了A就不能选同组的B)。
  • 可选性:每组可选择一个物品或不选(区别于“每组必须选一个”的变种)。
  • 与0/1背包的关联:可视为0/1背包的扩展——0/1背包中每组只有一个物品,分组背包中每组有多个物品。

分组背包的本质是“在组内互斥约束下的物品选择优化”,其解法是0/1背包的自然延伸。

二、分组背包的状态设计与递推

2.1 状态定义

沿用背包问题的经典状态设计,聚焦“容量”和“组数”两个维度:

  • dp[i][j]表示“考虑前i组物品,背包容量为j时的最大价值”。
  • 目标是dp[m][C](考虑所有m组,容量C时的最大价值)。

2.2 递推关系

对于第i组,有两种选择:不选该组任何物品选该组的某一个物品

  1. 不选第i:价值与前i-1组相同 →
    dp[i][j] = dp[i-1][j]

  2. 选第i组的第k个物品(需满足j ≥ w[i][k]):
    价值 = 前i-1组在容量j - w[i][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] | 第i组的所有物品k且j ≥ w[i][k] })

2.3 遍历顺序

与0/1背包类似,为避免同一组物品被多次选择,需按以下顺序遍历:

  1. 外层遍历组:从第1组到第m组(确保每组只处理一次)。
  2. 中层遍历容量:从C逆序到0(避免同一组内的物品被重复选择)。
  3. 内层遍历组内物品:对当前组的每个物品,更新容量j的最大价值。

三、代码实现

3.1 基础二维DP实现

public class GroupKnapsack {/*** 分组背包基础实现(二维DP)* @param m 组数* @param C 背包容量* @param groups 物品组(groups[i]是第i组的物品列表,每个物品为{w, v})* @return 最大价值*/public int maxValue(int m, int C, List<List<int[]>> groups) {// 二维DP数组:dp[i][j] = 前i组,容量j时的最大价值int[][] dp = new int[m + 1][C + 1];// 遍历每组物品(i从1到m)for (int i = 1; i <= m; i++) {List<int[]> group = groups.get(i - 1); // 第i组(0基索引)// 遍历容量(逆序,避免同一组物品重复选择)for (int j = C; j >= 0; j--) {// 情况1:不选第i组,价值继承前i-1组dp[i][j] = dp[i - 1][j];// 情况2:选第i组的某个物品for (int[] item : group) {int w = item[0];int v = item[1];if (j >= w) { // 容量足够dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - w] + v);}}}}return dp[m][C];}public static void main(String[] args) {GroupKnapsack solver = new GroupKnapsack();int m = 2; // 2组int C = 5; // 容量5// 初始化物品组:第0组[(2,3), (3,5)],第1组[(1,2), (4,6)]List<List<int[]>> groups = new ArrayList<>();groups.add(Arrays.asList(new int[]{2, 3}, new int[]{3, 5}));groups.add(Arrays.asList(new int[]{1, 2}, new int[]{4, 6}));System.out.println(solver.maxValue(m, C, groups)); // 输出7(3+5组选3,5;1+2组选1,2 → 5+2=7)}
}

3.2 空间优化:一维DP实现

观察二维DP可知,dp[i][j]仅依赖dp[i-1][j](上一组的状态),因此可压缩为一维数组:

  • dp[j]表示“当前处理到第i组,容量j时的最大价值”。
  • 遍历组和容量的顺序不变,仅需用当前组的结果覆盖上一组的结果。
public int maxValueOptimized(int m, int C, List<List<int[]>> groups) {// 一维DP数组:dp[j] = 容量j时的最大价值(滚动更新)int[] dp = new int[C + 1];// 遍历每组物品for (int i = 0; i < m; i++) {List<int[]> group = groups.get(i);// 逆序遍历容量(关键:避免同一组物品被多次选择)for (int j = C; j >= 0; j--) {// 遍历组内物品(更新当前容量的最大价值)for (int[] item : group) {int w = item[0];int v = item[1];if (j >= w) {dp[j] = Math.max(dp[j], dp[j - w] + v);}}}}return dp[C];
}
优化说明:
  • 空间复杂度从O(m×C)降至O(C),尤其适合组数m较大的场景。
  • 逆序遍历容量是核心:确保处理第i组时,dp[j - w]仍是“前i-1组”的状态,避免同一组内的物品被重复选择(若正序遍历,可能多次选择同一组的多个物品)。

四、分组背包的变种与应用

4.1 变种问题

  1. 每组至少选择一个物品

    • 初始化dp[0...C]-∞(表示不可行),仅dp[0] = 0
    • 递推时确保每组至少选择一个物品(避免“不选”的情况)。
  2. 组内物品可选择多个(但不超过组内数量)

    • 结合多重背包的“二进制优化”,将组内物品拆分为“可选k个”的组合。
  3. 有依赖的分组背包

    • 组内物品存在依赖(如选择A才能选择B),需先处理依赖关系,再按分组背包逻辑求解。

4.2 应用场景

  • 套餐选择:从多个套餐(每组)中选一个,如“外卖套餐”“旅游套餐”。
  • 工具选型:从多类工具(每组)中选一种,如“编程语言(Python/Java)”“数据库(MySQL/PostgreSQL)”。
  • 资源分配:有限预算下从多类项目(每组)中选一个投资,最大化收益。

五、时间复杂度与优化

5.1 时间复杂度

设组数为m,每组物品数为k(平均),背包容量为C

  • 基础实现与优化实现的时间复杂度相同:O(m×k×C)
  • m=100k=10C=1000时,总操作次数为100×10×1000=10^6,效率可接受。

5.2 优化技巧

  1. 组内物品剪枝:若组内存在物品A(w1, v1)和物品B(w2, v2),且w1 ≤ w2v1 ≥ v2,则B为无效物品(选A更优),可删除B减少遍历次数。
  2. 容量上限优化:对每组物品,计算组内最小总重量,容量小于该值时可跳过处理。
  3. 并行计算:对独立的组(无依赖),可并行处理(实际应用中较少使用,因Java多线程开销可能抵消收益)。

总结
分组背包是处理“互斥选择”问题的高效模型,其核心是“按组遍历+逆序容量+组内物品选择”:

  1. 状态定义延续背包问题的经典设计,聚焦“组数”和“容量”。
  2. 逆序遍历容量是避免组内物品重复选择的关键。
  3. 最终结果需取所有容量的最大值(而非仅dp[C])。

That’s all, thanks for reading~~
觉得有用就点个赞、收进收藏夹吧!关注我,获取更多干货~

http://www.dtcms.com/a/296921.html

相关文章:

  • mac电脑(m1) - flask断点失效
  • Datawhale AI数据分析 作业2
  • 力扣1287:有序数组中出现次数超过25%的元素
  • Linux join命令快速从大文件中匹配内容
  • 构建 Odoo 18 移动端导航:深入解析 OWL 框架、操作与服务
  • P1013 [NOIP 1998 提高组] 进制位
  • 【算法】递归、搜索与回溯算法入门
  • 星痕共鸣数据分析2
  • 【Guava】1.1.我的报告
  • 移动前端开发与 Web 前端开发的区别
  • 电商接口常见误区与踩坑提醒
  • 3.SOAP
  • 跨境支付入门~国际支付结算(风控篇)
  • 酷狗最新版KG-DEVID 算法分析
  • Unity 时间抗锯齿(Temporal Antialiasing, TAA)技术解析
  • T-RO顶刊|单视角“找相似”,大阪大学提出新型点云描述符(C-FPFH),杂乱场景一抓一个准!
  • 2025国自然青基、面上会评结束,资助率或创新低,跌破11.19%!
  • 期货交易系统用户操作与应用逻辑全析
  • springboot实战demo2
  • 图像识别任务的边界正在改变
  • Linux系统编译安装PostgreSQL 12.8(含报错处理与配置热加载)
  • C++标准库算法实战指南
  • Linux 进程间通信:共享内存详解
  • 2025年人形机器人动捕技术研讨会于7月31日在京召开
  • 如何使用 pdfMake 中文字体
  • Next.js 中配置不同页面布局方案
  • 无锡市亨达电机盛装亮相 2025上海生物发酵展引关注
  • 深入理解大语言模型生成参数:temperature、top\_k、top\_p 等全解析
  • 首发即开源!DAWorkBench数据可视化分析软件正式发布!(附源码下载网址)
  • ubuntu安装teams解决方法