【算法 | 背包专题】分组背包(解题思路+题单)

分组背包

上一节,我们提到了什么是01背包,以及如何求解01背包:

  • 【算法 | 背包专题】01背包(状态定义+状态转移+解题流程+题单)

现在,我们来看分组背包。

分组背包问题是背包问题的一种变形。在这个问题中,物品被分成了若干组,每组中的物品相互冲突,也就是说,每组只能选择一个物品。这与01背包问题的主要区别在于,01背包问题中,每个物品都可以被选择,而在分组背包问题中,每组只能选择一个物品。

求解分组背包问题的基本思路是动态规划。我们可以定义一个二维数组dp[i][j],表示前i组物品,总重量不超过j的情况下的最大价值。然后,我们可以遍历每一组物品,对于每一组中的每一个物品,我们都尝试将其加入背包,更新dp[i][j]的值。

欸,这就是分组背包的求解思路,每一组物品都进行可能性展开,去尝试。

模板题

我们来看这道模板题。

通天之分组背包

题目背景

直达通天路·小 A 历险记第二篇

题目描述

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,表示物品的重量,利用价值,所属组数。

输出格式

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

样例

样例输入

45 3
10 10 1
10 5 1
50 400 2

样例输出

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 范围内。

状态定义

对于这道题(分组背包问题),我们使用动态规划来解决,定义一个二维数组 dp[i][j],表示前i组物品,总重量不超过j的情况下的最大价值。

解题思路

  1. 首先获取背包的总重量m和物品的数量n,然后读取每个物品的重量、价值和所属组号,接着,将物品按照组号进行排序(重要)。

  2. 接下来,我们计算出总共有多少个组(用变量group存储起来)。

  3. 初始化dp数组,dp[0][j] = 0,表示没有物品的情况下,任何重量的背包获取的价值都是0(在Java中默认值都是0)。

  4. 遍历每一组物品对于每一组,我们都尝试将组内的每一个物品加入背包,更新dp[i][j]的值。具体来说,如果物品的重量小于或等于j,那么我们可以选择将其加入背包,此时dp[i][j] = max(dp[i-1][j], dp[i-1][j-物品的重量] + 物品的价值)

  5. 最后,dp[group][m]就是我们要求的答案,表示前group组物品,总重量不超过m的情况下获取的最大价值。

参考代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;public class Main {public static int MAXN = 1001;public static int MAXM = 1001;public static int[][] arr = new int[MAXN][3]; // (体积, 价值, 所属组号)public static int n, m;public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StreamTokenizer in = new StreamTokenizer(br);PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));while (in.nextToken() != StreamTokenizer.TT_EOF) {m = (int) in.nval;in.nextToken();n = (int) in.nval;for (int i = 1; i <= n; i++) {in.nextToken();arr[i][0] = (int) in.nval; // 体积in.nextToken();arr[i][1] = (int) in.nval; // 价值in.nextToken();arr[i][2] = (int) in.nval; // 所属组号}// 将物品按照组号进行排序Arrays.sort(arr, 1, n + 1, (a, b) -> a[2] - b[2]);// 输出计算结果out.println(compute());}out.flush();out.close();br.close();}// dp[i][j]: 前i组里面,每组最多挑选一件,在容量不超过j的情况下,最大价值是多少?public static int compute() {// 首先我们计算总共有多少个组,编号从1开始int group = 1;for (int i = 2; i <= n; i++) {if (arr[i - 1][2] != arr[i][2]) {group++;}}int[][] dp = new int[group + 1][m + 1];for (int start = 1, end = 2, i = 1; start <= n; i++) {// 第i组的范围是 [start, end),注意,范围是左闭右开的while (end <= n && arr[end][2] == arr[start][2]) {end++;}// 当知晓第i组的范围之后,就可以进行动态规划了for (int j = 0; j <= m; j++) { // 遍历背包容量// 不选物品dp[i][j] = dp[i - 1][j];// 每一件物品都试一遍,k指的是物品编号for (int k = start; k < end; k++) {// 如果背包容量允许,则尝试if (j >= arr[k][0]) {dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - arr[k][0]] + arr[k][1]);}}}// 要记得更新 start !!!start = end++;}// 最后返回前group组里面,每组最多挑选一件,在容量不超过j的情况下,最大价值是多少?return dp[group][m];}
}

空间压缩

和 01 背包一样,我们也要对分组背包进行空间压缩,优化空间复杂度。

我们可以观察到,dp[i][j]的值只依赖于dp[i-1][j]dp[i-1][j-arr[k][0]],也就是说,计算dp[i][j]的值时,只需要知道第i-1组的信息,而不需要知道i-2i-3等更早的组的信息。因此,我们可以只用一个一维数组dp[j]来存储每一组的信息,当我们处理完一组物品后,就可以覆盖掉这一组的信息,用来存储下一组的信息。

另外,在遍历所有可能的背包容量,以便计算出每种容量下的最大价值时,我们要注意遍历的顺序(即从后往前遍历)。

这是因为我们是在原地更新dp数组,如果我们从前往后遍历背包容量,那么在计算dp[j]时,dp[j - arr[k][0]]可能已经被更新过了,这就导致我们使用的是错误的值。为了避免这个问题,我们需要从后往前遍历背包容量。这样,在计算dp[j]时,dp[j - arr[k][0]]还没有被更新,我们可以安全地使用它的值。

参考代码如下:

// dp[i]: 每组商品最多挑选一件,在容量不超过i的情况下,最大价值是多少?
public static int compute() {int[] dp = new int[m + 1];// 同样,第i组的范围是 [start, end),注意,范围是左闭右开的int start = 1, end = 2;while (start <= n) {// 首先,找到当前组的范围while (end <= n && arr[end][2] == arr[start][2]) {end++;}// 知道当前组的范围后,就可以开始动态规划了,注意背包容量需要从后往前遍历// 因为从前往后遍历的话,会覆盖掉之前的数据,进而影响到后续元素的遍历for (int j = m; j >= 0; j--) {for (int k = start; k < end; k++) {if (j >= arr[k][0]) {dp[j] = Math.max(dp[j], dp[j - arr[k][0]] + arr[k][1]);}}}// 不要忘了更新 startstart = end++;}return dp[m];
}

力扣题单

学完后,我们可以刷几道力扣题巩固一下。

题目题解
1155. 掷骰子等于目标和的方法数题解
1981. 最小化目标值与所选元素的差题解
2218. 从栈中取出 K 个硬币的最大面值和题解

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

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

相关文章

转化延迟预估

转化数据延迟久&#xff0c;但实时反馈至关重要涉及到模型预估准度&#xff0c;进而影响客户成本。 现状&#xff1a;超过12h的转化被视为负例&#xff0c;12h以内的为正例&#xff0c;这样会导致模型低估。公示如下&#xff1a; P ( 转化 ∣ 点击 ) 延迟 ≤ 12 h 的转化 未转…

eclipse怎么配置git

在Eclipse中配置Git的步骤如下&#xff1a; 安装Git插件。打开Eclipse&#xff0c;选择“Help”菜单&#xff0c;然后点击“Eclipse Marketplace”。在搜索框中输入“EGit”&#xff0c;选择Eclipse Git Team Provider&#xff0c;点击“Go”按钮&#xff0c;然后点击“Inst…

【LeetCode】热题100:LRU缓存

题目&#xff1a; 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&…

什么是人工智能?人工智能、机器学习、深度学习三者之间有什么关系吗?

深度学习是机器学习的一个分支。深度学习是机器学习的一部分&#xff0c;与机器学习的其他分支学科&#xff0c;以及统计学、人工智能等学科都有着紧密的联系。深度学习、机器学习、人工智能、统计学之间的关系如图1-4所示。 图1-4 深度学习、机器学习、人工智能、统计学之间的…

【C语言】指针篇(指针数组,数组指针,函数指针,一级、二级指针)

文章目录 一、指针基础1.什么是指针2.指针的定义和初始化3.指针的解引用4.野指针和空指针5.指针的类型6.指针的大小7.指针的运算8.指针和数组9.指针和字符串10.二级指针 二、指针数组和数组指针1.指针数组2.数组指针3.练习 三、数组传参和指针传参1.一维数组传参2.二维数组传参…

Maven POM元素解析

这是对Maven中使用的Maven项目描述符的引用。 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/…

GEE:基于CHIRPS数据集的累积降水量影像下载

作者:CSDN @ _养乐多_ 本文将介绍在 Google Earth Engine(GEE)平台上使用“UCSB-CHG/CHIRPS/DAILY”数据集计算某一段时期(某年/某个季节/某月)的累积降雨量图像,并下载。 结果如下图所示, 文章目录 一、核心函数二、代码链接三、完整代码一、核心函数 .sum() // 对影…

02 Php学习:变量

Php 变量声明 Php 变量赋值 在PHP中&#xff0c;变量赋值是指将一个值赋给一个变量。变量赋值是 PHP 中最基本和常见的操作之一&#xff0c;以下是关于变量赋值的详细说明和示例&#xff1a; 变量赋值语法&#xff1a; $variable value;$variable&#xff1a;要赋值的变量名…

速盾:游戏cdn什么意思

CDN&#xff08;Content Delivery Network&#xff09;是指内容分发网络&#xff0c;它是由一组位于世界各地的服务器组成的网络&#xff0c;用于将内容有效地传输给用户。游戏CDN&#xff0c;顾名思义&#xff0c;就是用于游戏内容分发的网络。 在传统的网络传输模式中&#…

【MATLAB源码-第38期】基于OFDM的块状导频和梳状导频误码率性能对比,以及LS/LMMSE两种信道估计方法以及不同调制方式对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 块状导频和梳状导频都是用于无线通信系统中信道估计的方法。 块状导频&#xff1a; 定义&#xff1a; 在频域上&#xff0c;块状导频是连续放置的一组导频符号。这意味着所有的导频符号都集中在一个短的时间段内发送。 优点…

【Java编程进阶之路 11】Java内存管理深度剖析:垃圾回收机制与性能优化

文章目录 Java内存管理深度剖析&#xff1a;垃圾回收机制与性能优化引言01 Java内存管理基础1.1 堆&#xff08;Heap&#xff09;1.2 栈&#xff08;Stack&#xff09;1.3 方法区&#xff08;Method Area&#xff09;1.4 为什么需要垃圾回收&#xff1f; 02 垃圾回收的重要性2.…

php站长在线工具箱源码优化版

环境要求 PHP > 7.4MySQL > 5.6fileinfo扩展使用Redis缓存需安装Redis扩展 源码下载地址&#xff1a;php站长在线工具箱源码优化版.zip

计算机组成原理核心知识——计算机系统工作

目录 一、计算机运算&#xff08;一&#xff09;符号数和无符号数&#xff08;二&#xff09;定点数和浮点数&#xff08;三&#xff09;原码、反码、补码和移码 二、总线&#xff08;一&#xff09;系统总线&#xff08;二&#xff09;通信总线&#xff08;三&#xff09;单/多…

RISC-V特权架构 - 模式切换与委托

RISC-V特权架构 - 模式切换与委托 1 导致模式切换的常见动作2 异常处理规则3 异常处理时模式切换3.1 在U模式下&#xff0c;发生异常3.2 在S模式下&#xff0c;发生异常3.3 在M模式下&#xff0c;发生异常 4 系统调用时模式切换5 中断处理时模式切换 本文属于《 RISC-V指令集基…

PaddleVideo:onnx模型导出

本文节介绍 PP-TSM 模型如何转化为 ONNX 模型&#xff0c;并基于 ONNX 引擎预测。 1&#xff1a;环境准备 安装 Paddle2ONNX python -m pip install paddle2onnx 安装 ONNXRuntime # 建议安装 1.9.0 版本&#xff0c;可根据环境更换版本号 python -m pip install onnxrunti…

CSS:CSS的基础了解

css概述 CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09; 是用于控制网页样式和布局的一种样式表语言。用于描述网页的样式和布局&#xff0c;包括字体、颜色、大小、间距、边框等方面。 前端三&#x1f5e1;客&#xff1a;HTML,CSS,JavaScript&am…

GEE:样本点的样式设置

作者:CSDN @ _养乐多_ 本文将介绍在Google Earth Engine (GEE)平台上为样本点设置样式的方法和代码,样本点可以设置成任何颜色,以及7种形状,以便更直观了解数据的分布和特征。 文章目录 一、统一设置样式1.1 示例代码1.2 示例代码链接二、每一类一个样式2.1 示例代码2.2…

jenv实现mac下多版本jdk管理以及切换

文章目录 背景步骤 背景 多个java项目使用jdk版本不一样,mac机器下需要实现多版本的jdk管理. 使用工具jenv 步骤 oracle javase 官网选择合适的jdk版本,比如 我使用jdk11 下载正确的 macOS Arm 64 dmg版本. 然后在mac系统下双击dmg包进行安装. 安装好之后jdk路径为 /Library…

7款公司电脑监控软件

7款公司电脑监控软件 研究证明&#xff0c;人们在家办公的效率比在办公室办公的效率低一半&#xff0c;其中原因是缺少监督&#xff0c;即便在公司办公&#xff0c;还存在员工偷闲的时刻&#xff0c;比如聊天、浏览无关网站、看剧、炒股等&#xff0c;企业想提高员工的工作效率…

【Java】maven传递依赖冲突解决

传递依赖的概念&#xff1a; 传递依赖:&#xff1a; A.jar 依赖 B.jar, B.jar 依赖 C.jar, 这个时候我们就说B是A的直接依赖, C是A传递依赖; 传递依赖可能会产生冲突: 联系着上面, 新导入一个jar包D.jar, D依赖C.jar, 但是B依赖的1.1版本, 而D依赖的是1.2版本, 这时候C这个j…