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

背包DP之混合背包

背包DP之混合背包

    • 一、混合背包基础认知
      • 1.1 问题定义
      • 1.2 核心特征
    • 二、求解框架:分类处理+统一DP
      • 2.1 核心思路
      • 2.2 状态定义与递推
    • 三、代码实现
      • 3.1 完整实现
      • 3.2 代码解析
    • 四、实例推演
      • 4.1 输入物品
      • 4.2 DP更新过程
    • 五、混合背包的变种与优化
      • 5.1 变种问题
      • 5.2 优化技巧
      • 三类背包物品处理逻辑对比
      • 总结

实际的背包问题中,物品往往不会是单一类型——可能既有“只能选一次”的0/1背包物品,又有“可无限选”的完全背包物品,还有“最多选k次”的多重背包物品,这种“混合背包”模型更贴近现实场景(如“限量商品+常规商品+限购商品”的组合),但求解时需要整合多种背包的处理逻辑。

一、混合背包基础认知

1.1 问题定义

给定n种物品,每种物品属于以下三类之一:

  • 0/1背包物品:最多选择1次(如限量纪念品);
  • 完全背包物品:可无限选择(如常规商品);
  • 多重背包物品:最多选择cnt[i]次(如限购3件的促销商品)。

每种物品有重量w[i]和价值v[i],背包容量为C。求在总重量不超过C的前提下,能装入物品的最大总价值。

示例:

  • 输入:n=3, C=10
    • 物品0(0/1):w=3, v=5(最多1次)
    • 物品1(完全):w=2, v=3(无限次)
    • 物品2(多重):w=4, v=6, cnt=2(最多2次)
  • 输出:17(物品0×1 + 物品1×1 + 物品2×2 → 重量3+2+8=13>10;正确最优:物品1×3(2×3=6重量,3×3=9) + 物品2×1(4重量,6) → 总重量10,价值15;或物品2×2(8重量,12) + 物品1×1(2重量,3) → 总重量10,价值15?实际最优为物品0(3,5)+物品2×2(8,12)→ 重量11>10,因此正确输出15)

1.2 核心特征

  • 多类型共存:物品分三类,需针对每种类型采用对应处理逻辑。
  • 统一约束:所有物品共享背包容量C,总重量不得超过C
  • 求解难点:如何在同一框架中整合0/1、完全、多重背包的处理逻辑,避免冲突。

混合背包的本质是“多类型物品的容量分配优化”,核心思路是“分类处理,统一更新”——将所有物品转化为可按统一规则处理的形式,再用背包DP求解。

二、求解框架:分类处理+统一DP

2.1 核心思路

混合背包的求解可分为两步:

  1. 标准化处理:将三类物品转化为“可按统一规则处理的形式”:

    • 0/1背包物品:直接保留(本身符合“最多1次”规则)。
    • 完全背包物品:保留(需按“无限次”规则处理)。
    • 多重背包物品:通过“二进制拆分”转化为0/1背包物品(拆分后符合“最多1次”规则)。
  2. 统一DP更新:用一维DP数组,根据物品类型选择遍历顺序(0/1和拆分后的多重物品用逆序,完全物品用正序),累计更新最大价值。

2.2 状态定义与递推

沿用一维DP数组:

  • dp[j]表示“容量为j时的最大价值”。
  • 递推逻辑根据物品类型调整:
    • 0/1或拆分后的多重物品:逆序遍历容量jdp[j] = max(dp[j], dp[j - w] + v)(避免重复选择)。
    • 完全背包物品:正序遍历容量jdp[j] = max(dp[j], dp[j - w] + v)(允许重复选择)。

三、代码实现

3.1 完整实现

import java.util.*;public class HybridKnapsack {// 物品类型枚举enum ItemType {ZERO_ONE,    // 0/1背包UNBOUNDED,   // 完全背包MULTIPLE     // 多重背包}// 物品类static class Item {int w;         // 重量int v;         // 价值ItemType type; // 类型int cnt;       // 数量(仅多重背包用)public Item(int w, int v, ItemType type) {this(w, v, type, 0);}public Item(int w, int v, ItemType type, int cnt) {this.w = w;this.v = v;this.type = type;this.cnt = cnt;}}/*** 混合背包求解* @param items 物品列表(含三类物品)* @param C 背包容量* @return 最大价值*/public int maxValue(List<Item> items, int C) {int[] dp = new int[C + 1];for (Item item : items) {int w = item.w;int v = item.v;switch (item.type) {case ZERO_ONE:// 0/1背包:逆序遍历for (int j = C; j >= w; j--) {dp[j] = Math.max(dp[j], dp[j - w] + v);}break;case UNBOUNDED:// 完全背包:正序遍历for (int j = w; j <= C; j++) {dp[j] = Math.max(dp[j], dp[j - w] + v);}break;case MULTIPLE:// 多重背包:二进制拆分后按0/1处理List<Item> splitItems = splitMultipleItem(item);for (Item split : splitItems) {int sw = split.w;int sv = split.v;for (int j = C; j >= sw; j--) {dp[j] = Math.max(dp[j], dp[j - sw] + sv);}}break;}}return dp[C];}// 二进制拆分多重物品private List<Item> splitMultipleItem(Item multipleItem) {List<Item> splitItems = new ArrayList<>();int w = multipleItem.w;int v = multipleItem.v;int cnt = multipleItem.cnt;for (int k = 1; k <= cnt; k *= 2) {splitItems.add(new Item(k * w, k * v, ItemType.ZERO_ONE));cnt -= k;}if (cnt > 0) {splitItems.add(new Item(cnt * w, cnt * v, ItemType.ZERO_ONE));}return splitItems;}public static void main(String[] args) {HybridKnapsack solver = new HybridKnapsack();List<Item> items = new ArrayList<>();// 物品0:0/1背包(w=3, v=5)items.add(new Item(3, 5, ItemType.ZERO_ONE));// 物品1:完全背包(w=2, v=3)items.add(new Item(2, 3, ItemType.UNBOUNDED));// 物品2:多重背包(w=4, v=6, cnt=2)items.add(new Item(4, 6, ItemType.MULTIPLE, 2));int C = 10;System.out.println(solver.maxValue(items, C)); // 输出15}
}

3.2 代码解析

  1. 物品分类:用ItemType枚举区分三类物品,Item类存储重量、价值及类型(多重物品额外存储数量)。
  2. 分类处理
    • 0/1物品:逆序遍历容量,避免重复选择。
    • 完全物品:正序遍历容量,允许无限选择。
    • 多重物品:先通过splitMultipleItem二进制拆分(转化为0/1物品),再逆序遍历。
  3. 统一更新:所有物品最终通过一维DP数组更新,累计最大价值。

四、实例推演

4.1 输入物品

  • 物品0(0/1):w=3, v=5
  • 物品1(完全):w=2, v=3
  • 物品2(多重,cnt=2):w=4, v=6 → 拆分后为(4,6)(4,6)(0/1物品)。

4.2 DP更新过程

  1. 初始状态dp = [0,0,0,0,0,0,0,0,0,0,0](容量0~10)。

  2. 处理物品0(0/1)

    • 逆序遍历j=10→3
      • j=3dp[3] = max(0, dp[0]+5) =5
      • j=4~10dp[j] = max(0, dp[j-3]+5)(如j=5dp[2]+5=5
    • 处理后:dp = [0,0,0,5,5,5,5,5,5,5,5]
  3. 处理物品1(完全)

    • 正序遍历j=2→10
      • j=2dp[2] = max(0, dp[0]+3)=3
      • j=4dp[4] = max(5, dp[2]+3)=6
      • j=6dp[6] = max(5, dp[4]+3)=9
      • j=8dp[8] = max(5, dp[6]+3)=12
      • j=10dp[10] = max(5, dp[8]+3)=15
    • 处理后:dp = [0,0,3,5,6,8,9,11,12,14,15]
  4. 处理物品2(拆分后两个0/1物品)

    • 第一个拆分物品(4,6)
      • 逆序遍历j=10→4
        • j=10dp[10] = max(15, dp[6]+6)=9+6=15(不变)
        • j=8dp[8] = max(12, dp[4]+6)=6+6=12(不变)
    • 第二个拆分物品(4,6)
      • 逆序遍历j=10→4
        • j=10dp[10] = max(15, dp[6]+6)=15(不变)
        • j=8dp[8] = max(12, dp[4]+6)=12(不变)
    • 最终dp[10] =15

五、混合背包的变种与优化

5.1 变种问题

  1. 二维约束混合背包

    • 问题:物品有重量和体积两个约束,需同时满足。
    • 解法:用二维DP数组dp[j][k]j重量,k体积),按类型选择遍历顺序(0/1和多重逆序,完全正序)。
  2. 带依赖的混合背包

    • 问题:部分物品有依赖(如选B必须先选A),且依赖物品可能分属不同类型。
    • 解法:先处理依赖关系(如树形结构),再按类型处理每个节点的物品。
  3. 最大化数量而非价值

    • 问题:目标是最大化物品数量(而非价值),有价值上限约束。
    • 解法:交换价值和重量的角色(重量设为价值,价值设为1),按原框架求解。

5.2 优化技巧

  1. 预处理剪枝

    • 移除“无效物品”:若物品A(w1,v1)和物品B(w2,v2)同类型,且w1≥w2v1≤v2,则A无效(选B更优)。
    • 容量上限优化:对完全背包物品,若w > C,可直接忽略(无法装入)。
  2. 拆分优化

    • 多重物品拆分时,若w×cnt ≤ C,可按完全背包处理(减少拆分次数)。
    • 例:物品w=2, cnt=5, C=102×5=10 ≤10,可视为完全背包(反正最多选5次,正序遍历自然不会超过)。
  3. 遍历顺序调整

    • 先处理0/1和多重物品(逆序),再处理完全物品(正序),避免完全物品的正序更新干扰前两类物品的状态。

三类背包物品处理逻辑对比

物品类型核心特征处理逻辑遍历顺序
0/1背包最多选1次直接按0/1规则更新逆序(C→w
完全背包可无限选按完全规则更新正序(w→C
多重背包最多选cnt二进制拆分后按0/1规则更新逆序(C→w

总结

混合背包的核心是“分类处理,统一整合”——通过将不同类型的物品转化为可按对应规则处理的形式,在同一DP框架中累计更新最大价值。注意点如下:

  1. 类型识别:准确区分物品类型,选择对应处理逻辑(0/1逆序、完全正序、多重拆分)。
  2. 拆分技巧:多重物品通过二进制拆分转化为0/1物品,平衡效率与复杂度。
  3. 统一更新:用一维DP数组作为载体,按类型调整遍历顺序,避免状态冲突。

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

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

相关文章:

  • 解决安装anaconda3后如何打开anaconda navigator的问题
  • 解构远程智能系统的视频能力链:从RTSP|RTMP协议接入到Unity3D头显呈现全流程指南
  • 力扣刷题(第一百零一天)
  • 0728 哈希表折半查找树二叉树
  • 【mysql】创建视图查询当月累计销售额的案例
  • python案例分析:基于新能源汽车论坛评价数据情感分析的客户满意度研究,文本挖掘包括lda主题分析和词频分析、情感分析、网络语义分析
  • 搜索二维矩阵Ⅱ C++
  • 【无标题】暗物质衰减现象解释
  • 二十一、动植物类(自然生态)
  • 鱼皮项目简易版 RPC 框架开发(三)
  • Python 实现多服务器并发启动 SDK-C Master 与 Viewer 的分布式方案
  • [尚庭公寓]15-个人中心
  • 力扣-22.括号生成
  • C++初学者4——标准数据类型
  • JavaScript对象与Math对象完全指南
  • 力扣7:整数反转
  • 利用DataStream和TrafficPeak实现大数据可观察性
  • jQuery 最新语法大全详解(2025版)
  • 下载k8s官方组件chart和容器镜像
  • JavaScript中的Promise.all方法详解
  • 坚鹏:AI智能体培训是知行学成为AI智能体创新应用引领者的基础
  • 【归并排序】排序数组(medium)
  • 阿里云【免费试用】Elasticsearch 智能运维 AI 助手
  • 应用信息更新至1.18.0
  • 加法器 以及ALU(逻辑算术单元)
  • 深入解析 Spring 获取 XML 验证模式的过程
  • redis数据库的四种取得 shell方法
  • C++模板进阶:从基础到实战的深度探索
  • python生成 requirement.txt 文件
  • 一个高效的阿里云漏洞库爬虫工具,用于自动化爬取和处理CVE数据