【背包dp-----分组背包】------(标准的分组背包【可以不装满的 最大价值】)

通天之分组背包

题目链接

题目描述

01 01 01 背包问世之后,小 A 对此深感兴趣。一天,小 A 去远游,却发现他的背包不同于 01 01 01 背包,他的物品大致可分为 k k k 组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。

输入格式

两个数 m , n m,n m,n,表示一共有 n n n 件物品,总重量为 m m m

接下来 n n n 行,每行 3 3 3 个数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,表示物品的重量,利用价值,所属组数。

输出格式

一个数,最大的利用价值。

输入输出样例 #1

输入 #1

45 3
10 10 1
10 5 1
50 400 2

输出 #1

10

说明/提示

0 ≤ m ≤ 1000 0 \leq m \leq 1000 0m1000 1 ≤ n ≤ 1000 1 \leq n \leq 1000 1n1000 1 ≤ k ≤ 100 1\leq k\leq 100 1k100 a i , b i , c i a_i, b_i, c_i ai,bi,ciint 范围内。

\

0、分组背包特点

  • 每组内只能选 一个物品 或者 不选任何物品
  • 每组之间是独立的,可以按顺序处理;
  • 对每组分别进行一次 0-1 背包更新;
  • 最终使用动态规划来记录状态。

题解(二维dp)


一、状态定义

我们使用二维动态规划数组:

dp[i][j] 表示从前 i物品组中,在总容量恰好为 j 的情况下所能获得的最大价值。


二、 初始化分析

for(ll j=0;j<=m;j++)
{dp[0][j]=0;
}
  • 第 0 组表示“没有物品可选”,此时无论容量是多少,最大价值都为 0;
  • 其他位置初始化为 LLONG_MIN,表示不可达状态。

三、状态转移方程

对于当前组中的每一个物品 {w, v}(重量和价值),我们尝试将其加入到容量 j 的背包中:

if (j >= w) 
{dp[i][j] = max(dp[i][j], dp[i - 1][j - w] + v);
}

这个状态转移的意思是:

  • 如果当前容量 j 可以装下当前物品,则从上一组的容量 j - w 状态中转移过来,并加上该物品的价值;
  • 否则保持不变(继承上一组的状态)。

四、实现细节

1.数据结构说明

map<ll, vector<pair<ll, ll>>> groups;
  • 使用 map 来将物品按组号分类;
  • 每个组是一个 vector<pair<weight, value>>,存储该组中的所有物品。

2.动态规划数组初始化

vector<vector<ll>> dp(group_count + 1, vector<ll>(m + 1, LLONG_MIN));
for(ll j=0;j<=m;j++)
{dp[0][j]=0;
}
  • 初始时,除了第 0 组外,其他所有状态设为最小值 LLONG_MIN
  • 第 0 组初始化为全 0,表示不选任何物品时的价值。

3.遍历过程的主次顺序

3.1. 外层循环:遍历物品组
  • 主要任务:遍历每一个物品组(从第1组开始,跳过第0组)。
  • 实现方式
    for (auto it = (++groups.begin()); it != groups.end(); it++) 
    {ll gid = it->first;const vector<pair<ll, ll>>& items = it->second;// ...
    }
    
  • 说明:这里的 it 是指向当前物品组的迭代器,gid 是当前组的组号,items 是该组内所有物品的集合。
3.2. 继承上一组的状态
  • 主要任务:在处理当前组之前,首先继承前一组的状态(即假设不选择当前组中的任何物品)。
  • 实现方式
    for (ll j = 0; j <= m; j++) 
    {dp[gid][j] = dp[gid - 1][j];
    }
    
  • 说明这一步确保了即使当前组没有合适的物品可选或者我们决定不选当前组的任何物品时,我们的解不会变差。
3.3. 中层循环:遍历当前组内的每个物品
  • 主要任务:对于当前组中的每一个物品,尝试将其加入背包,并更新相应的状态。
  • 实现方式
    for (const auto& item : items) {ll w = item.first;  // 当前物品的重量ll v = item.second; // 当前物品的价值// ...
    }
    
3.4. 最内层循环:遍历背包容量
  • 主要任务:对于当前物品,遍历所有可能的背包容量 j,并根据当前物品的重量和价值更新 dp 数组。
  • 实现方式
    for (ll j = w; j <= m; j++) 
    { // 注意这里从 w 开始循环if (dp[gid - 1][j - w] != LLONG_MIN){dp[gid][j] = max(dp[gid][j], dp[gid - 1][j - w] + v);}
    }
    
  • 说明这里从 w 开始循环是因为只有当背包容量大于等于当前物品的重量时,才有可能将该物品放入背包。
3.5.总结遍历过程的主次顺序
  1. 外层循环:遍历物品组

    • 对于每一个物品组(从第1组开始),获取该组的所有物品。
  2. 继承上一组的状态

    • 在处理当前组之前,先继承前一组的状态(即假设不选择当前组中的任何物品),确保基础状态正确。
  3. 中层循环:遍历当前组内的每个物品

    • 对于当前组中的每一个物品,计算其对背包的影响。
  4. 最内层循环:遍历背包容量

    • 对于当前物品,遍历所有可能的背包容量,并根据当前物品的重量和价值更新 dp 数组。

五、完整代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;int main() 
{// m 表示背包的最大容量// n 表示物品总数ll m, n;cin >> m >> n;// 使用 map 将物品按组号分类存储// groups[group_id] 存储该组下的所有物品 {weight, value}map<ll, vector<pair<ll, ll>>> groups;// 插入一个无效的第0组,使后续组号可以直接和 dp 数组下标对应groups[0].push_back({0, 0});// 输入每个物品的信息:重量、价值、所属组号for (ll i = 0; i < n; i++) {ll weight, value, group_id;cin >> weight >> value >> group_id;groups[group_id].push_back({weight, value});}// 动态规划数组 dp[i][j]// dp[i][j] 表示前 i 组物品,在容量 j 下可以获得的最大价值// 初始化为 LLONG_MIN 表示不可达状态ll group_count = groups.size() - 1; // 减去人为添加的第0组vector<vector<ll>> dp(group_count + 1, vector<ll>(m + 1, LLONG_MIN));// 初始状态:前 0 组(没有选任何物品)时,所有容量下的最大价值都是 0for (ll j = 0; j <= m; j++) {dp[0][j] = 0;}// 遍历每一组(从实际的第1组开始)// 注意这里使用 ++groups.begin() 跳过第0组for (auto it = (++groups.begin()); it != groups.end(); it++) {ll gid = it->first;                     // 当前组号const vector<pair<ll, ll>>& items = it->second; // 该组的所有物品// 先继承上一组的状态(不选当前组中的任何物品)for (ll j = 0; j <= m; j++) {dp[gid][j] = dp[gid - 1][j];}// 对当前组中的每一个物品尝试选择for (const auto& item : items) {ll w = item.first;   // 当前物品的重量ll v = item.second;  // 当前物品的价值// 遍历所有可能的背包容量 j,从当前物品的重量开始for (ll j = w; j <= m; j++) {// 如果上一组 j - w 容量是可达的,则更新当前组 j 容量下的最大价值if (dp[gid - 1][j - w] != LLONG_MIN) {dp[gid][j] = max(dp[gid][j], dp[gid - 1][j - w] + v);}}}}// 输出结果:最后一组在最大容量 m 下所能获得的最大价值cout << dp[group_count][m] << endl;return 0;
}

六、 时间与空间复杂度分析

类型复杂度
时间复杂度 O ( G ⋅ m ⋅ K ) O(G \cdot m \cdot K) O(GmK)
空间复杂度 O ( G ⋅ m ) O(G \cdot m) O(Gm)

其中:

  • G G G:物品组数;
  • m m m:背包容量;
  • K K K:平均每组中的物品数量。

题解(滚动数组优化)

一、 动态规划数组 dp[j]

  • 定义dp[j] 表示在容量为 j 的情况下可以获得的最大价值。
  • 初始化:所有值初始化为 LLONG_MIN(表示不可达状态),除了 dp[0] = 0(容量为 0 时的最大价值为 0)。

2. 遍历主次顺序

2.1主循环:遍历每一组物品
for (auto it = groups.begin(); it != groups.end(); it++) 
{const vector<pair<ll, ll>>& items = it->second;
  • 遍历顺序:我们首先遍历每一个物品组,从第一个组到最后一个组依次处理。
  • 原因:这样可以确保每组的状态转移依赖于前一组的状态,从而逐步构建最终的最优解。
2.2次循环:倒序处理容量 j
vector<ll> temp_dp(dp); // 先继承上一组的状态for (ll j = m; j >= 0; j--) 
{for (const auto& item : items) {ll weight = item.first;ll value = item.second;if (j >= weight && dp[j - weight] != LLONG_MIN) {temp_dp[j] = max(temp_dp[j], dp[j - weight] + value);}}
}
  • 倒序处理容量 j:从大到小遍历容量 j,确保每次更新不会覆盖尚未使用的旧值。
  • 原因:因为在动态规划中,我们需要基于未被当前操作修改过的旧值进行计算,倒序遍历可以避免数据覆盖的问题。

3. 使用临时 dp 数组

vector<ll> temp_dp(dp); // 先继承上一组的状态
  • 为什么需要临时 dp 数组

    • 在处理每一组时,我们需要先保存上一组的状态(即假设不选择当前组中的任何物品)。
    • 如果直接在 dp 上进行更新,会导致后续的操作基于已经被修改的状态,从而影响结果的正确性。
    • 使用临时数组 temp_dp 可以确保我们在处理当前组的所有物品之前,已经完整地保存了上一组的状态。
  • 合并 temp_dpdp

    • 在处理完当前组的所有物品之后,我们将 temp_dp 合并回 dp,以完成状态转移。

4. 输出结果

cout << *max_element(dp.begin(), dp.end()) << endl;
  • 输出最大价值:由于我们的目标是找到最大可能的价值,而不是仅限于给定容量 m,所以我们使用 *max_element(dp.begin(), dp.end()) 来获取整个 dp 数组中的最大值。

5.完整代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;int main() 
{// 输入 m 表示背包的最大容量// n 表示物品总数ll m, n; cin >> m >> n;// 使用 map 存储每组物品:// group_id -> vector<pair<weight, value>> 表示每个组中的所有物品map<ll, vector<pair<ll, ll>>> groups;// 读取每个物品的信息:重量、价值、所属组号for (ll i = 0; i < n; i++) {ll weight, value, group_id;cin >> weight >> value >> group_id;groups[group_id].push_back(make_pair(weight, value));}// 动态规划数组 dp[j]:// 表示当前状态下,容量为 j 时可以获得的最大价值// 初始值设为 LLONG_MIN 表示不可达的状态// dp[0] 初始化为 0 表示容量为 0 时的价值为 0vector<ll> dp(m + 1, LLONG_MIN);dp[0] = 0;// 遍历每一组物品for (auto it = groups.begin();it != groups.end(); ++it) {// 当前组的所有物品列表const vector<pair<ll, ll>>& items = it->second;// 创建 temp_dp 作为当前组的临时状态数组// temp_dp 初始化为当前 dp 的值(继承上一组的状态)// 这样做是为了保证我们可以不选当前组中的任何物品vector<ll> temp_dp(dp);// 倒序遍历容量 j(从大到小)// 原因:防止在更新 temp_dp[j] 时,前面已经计算过的结果被覆盖for (ll j = m; j >= 0; j--) {// 尝试从当前组中选择每一个物品for (const auto& item : items) {ll weight = item.first;ll value = item.second;// 如果当前容量 j 足够装下这个物品,并且 j - weight 状态可达if (j >= weight && dp[j - weight] != LLONG_MIN) {// 更新 temp_dp[j] 为:// 前一组容量 j - weight 的最大价值 + 当前物品价值// 取 max 是因为可能有多个物品竞争同一个 j 容量temp_dp[j] = max(temp_dp[j], dp[j - weight] + value);}}}// 将 temp_dp 合并回 dp 中,表示处理完这一组后的最终状态dp = temp_dp;}// 输出结果:整个 dp 数组中的最大值(不一定刚好用满容量 m)cout << *max_element(dp.begin(), dp.end()) << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/81419.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

操作系统:os概述

操作系统&#xff1a;OS概述 程序、进程与线程无极二级目录三级目录 程序、进程与线程 指令执行需要那些条件&#xff1f;CPU内存 需要数据和 无极 二级目录 三级目录

RAG文本分块

不论是向量化模型还是大语言模型&#xff0c;都存在输入长度的限制。对于超过限制的文本&#xff0c;模型会进行截断&#xff0c;造成语义缺失。分块可以确保每个文本片段都在模型的处理范围内&#xff0c;避免重要信息的丢失。 文本分块的核心原则 高质量分块的核心原则是&a…

2025 年九江市第二十三届中职学校技能大赛 (网络安全)赛项竞赛样题

2025 年九江市第二十三届中职学校技能大赛 &#xff08;网络安全&#xff09;赛项竞赛样题 &#xff08;二&#xff09;A 模块基础设施设置/安全加固&#xff08;200 分&#xff09;A-1 任务一登录安全加固&#xff08;Windows,Linux&#xff09;A-2 任务二 Nginx 安全策略&…

量子隧穿:PROFINET到Ethernet ip的无损耗协议转换方案转

在本季度的生产工作中&#xff0c;我们成功实现了仓储物流自动化分拣系统中的关键技术突破。我们面临的主要挑战是将采用EtherNet/IP协议的输送带控制器与PROFINET协议的上位系统进行有效通信。通过引入ethernet IP转PROFINET网关倍讯科技BX-606-EIP&#xff0c;我们实现了输送…

OpenCV CUDA模块中矩阵操作------降维操作

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::reduce 函数用于对 GPU 上的矩阵沿某个维度进行降维操作&#xff0c;例如求和、取最大值等。此函数支持多种降维操作&#xff0c;并允…

一分钟用 MCP 上线一个 贪吃蛇 小游戏(CodeBuddy版)

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 你好&#xff0c;我是悟空。 背景 上篇我们用 MCP 上线了一个 2048 小游戏&#xff0c;这次我们继续做一个 …

简单神经网络(ANN)实现:从零开始构建第一个模型

本文将手把手带你用 Python Numpy 实现一个最基础的人工神经网络&#xff08;Artificial Neural Network, ANN&#xff09;。不依赖任何深度学习框架&#xff0c;适合入门理解神经网络的本质。 一、项目目标 构建一个三层神经网络&#xff08;输入层、隐藏层、输出层&#xf…

使用python进行人员轨迹跟踪

一、系统概述 该系统基于计算机视觉技术&#xff0c;实现对视频或摄像头画面中的人员进行检测、跟踪&#xff0c;并生成轨迹数据。支持透视变换校准&#xff08;鸟瞰图显示&#xff09;、多目标跟踪、轨迹存储及视频录制功能&#xff0c;适用于安防监控、行为分析等场景。 二…

[强化学习的数学原理—赵世钰老师]学习笔记02-贝尔曼方程

本人为强化学习小白&#xff0c;为了在后续科研的过程中能够较好的结合强化学习来做相关研究&#xff0c;特意买了西湖大学赵世钰老师撰写的《强化学习数学原理》中文版这本书&#xff0c;并结合赵老师的讲解视频来学习和更深刻的理解强化学习相关概念&#xff0c;知识和算法技…

Docker入门指南:镜像、容器与仓库的核心概念解析

目录 前言&#xff1a;为什么需要Docker&#xff1f; 一、Docker能做什么&#xff1f; 二、核心概念解析 1. 镜像&#xff08;Image&#xff09;&#xff1a;应用的标准化打包 2. 容器&#xff08;Container&#xff09;&#xff1a;镜像的运行实例 3. 镜像仓库&#xff0…

大模型微调实战:基于GpuGeek平台的低成本高效训练方案

文章目录 引言一、GpuGeek平台使用入门1. 注册与账号设置2. 控制台功能概览3. 快速创建GPU实例3. 预置镜像与自定义环境 二、GpuGeek平台核心优势解析1. 显卡资源充足&#xff1a;多卡并行加速训练2. 镜像超多&#xff1a;开箱即用的开发环境3. 计费灵活&#xff1a;按需付费降…

Linux:计算机的层状结构

1.冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本、台式机。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系结构。 CPU&#xff1a;运算器和控制器组成。运算器主要工作是做算术运算和逻辑运算。控制器主要工作是协调设备之间信息流动的…

LangGraph(四)——加入人机交互控制

目录 1. 引言2. 添加Human Assistance工具3. 编译状态图4. 提示聊天机器人5. 恢复执行参考 1. 引言 智能体可能不可靠&#xff0c;甚至需要人工输入才能完成任务。同样&#xff0c;对于某些操作&#xff0c;你可能需要在运行前获得人工批准&#xff0c;以保证一切按预期运行。 …

数据结构【AVL树】

AVL树 1.AVL树1.AVL的概念2.平衡因子 2.AVl树的实现2.1AVL树的结构2.2AVL树的插入2.3 旋转2.3.1 旋转的原则 1.AVL树 1.AVL的概念 AVL树可以是一个空树。 它的左右子树都是AVL树&#xff0c;且左右子树的高度差的绝对值不超过1。AVL树是一颗高度平衡搜索二叉树&#xff0c;通…

JavaScript【5】DOM模型

1.概述&#xff1a; DOM (Document Object Model)&#xff1a;当页面被加载时&#xff0c;浏览器会创建页面的文档对象模型&#xff0c;即dom对象&#xff1b;dom对象会被结构化为对象树&#xff0c;如一个HTML文档会被分为head&#xff0c;body等部分&#xff0c;而每个部分又…

STM32烧录程序正常,但是运行异常

一、硬件配置问题 BOOT引脚设置错误 STM32的启动模式由BOOT0和BOOT1引脚决定。若设置为从RAM启动&#xff08;BOOT01&#xff0c;BOOT10&#xff09;&#xff0c;程序在掉电后无法保存&#xff0c;导致复位后无法正常运行。应确保BOOT00&#xff08;从Flash启动&#xff09;15。…

汽车二自由度系统模型以及电动助力转向系统模型

汽车二自由度系统模型与电动助力转向系统&#xff08;EPS&#xff09;的详细建模方案&#xff0c;包含理论推导、MATLAB/Simulink实现代码及参数说明&#xff1a; 一、二自由度汽车模型 1. 模型描述 包含以下两个自由度&#xff1a; 横向运动&#xff08;侧向加速度&#xf…

git提交库常用词

新功能 feat修改BUG fix文档修改 docs格式修改 style重构 refactor性能提升 perf测试 test构建系统 build对CI配置文件修改 ci修改构建流程、或增加依赖库、工具 chore回滚版本 revert

JavaScript 时间转换:从 HH:mm:ss 到十进制小时及反向转换

关键点 JavaScript 可以轻松实现时间格式&#xff08;HH:mm:ss 或 HH:mm&#xff09;与十进制小时&#xff08;如 17.5&#xff09;的相互转换。两个函数分别处理时间字符串到十进制小时&#xff0c;以及十进制小时到时间字符串的转换&#xff0c;支持灵活的输入和输出格式。这…

LLM智能体新纪元:深入解析MCP与A2A协议,赋能智能自动化协作

LLM智能体&#xff08;LLM agents&#xff09;是能够自主行动以实现特定目标的AI系统。在实际应用中&#xff0c;智能体能够将用户请求拆解为多个步骤&#xff0c;利用知识库或API获取数据&#xff0c;最终整合出答案。这让智能体相比于传统独立聊天机器人拥有更强大的能力——…