背包问题----分组背包(超详细讲解)

2023-05-16

树形dp----分组背包+依赖背包

  • 分组背包
    • 1.定义
    • 2.讲解
    • 3.练习题
  • 依赖背包
    • 1.定义
    • 2.讲解
    • 3.练习题

分组背包

1.定义

分组背包,通俗的讲就是,给你N组物品,然后每一组你至多选择一个物品(也可以不选),每个物品都有自己的体积和价值,现在给你一个容里为M的背包,让你用这个背包装物品,使得物品价值总和最大.

2.讲解

其实就类似于01背包,对于一个物品有两种决策选或不选,但是分组背包是在01背包的基础上对物品进行了分组,并且每一组只能最多选择一个物品,所以我们不妨用01背包的思想去思考分组背包.

分析:我们设f[i][j]为当前考虑到了第i组物品,剩余容里为j的背包能装物品的最大价值,那么很容易想到我们需要去枚举第i组物品,考虑选哪一个物品时最优的(或者不选),状态转移方程就是 i f ( j > = v [ i ] [ k ] ) f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ j − v [ i ] [ k ] ] + w [ i ] [ k ] ) if(j>=v[i][k]) f[i][j] = max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]) if(j>=v[i][k])f[i][j]=max(f[i][j],f[i1][jv[i][k]]+w[i][k]),v[i][k]和w[i][k]分别表示第i组物品中第k个物品的体积和价值

代码:

for(int i=1;i<=n;i++)
	 for(int j=0;j<=m;j++)
	  for(int k=1;k<=s[i];k++)//s[i]表示第i组物品的个数
	   if(j>=v[i][k])//剩余的背包容量j大于第i组的第k个物品的体积 
	   {
	   	  f[i][j] = max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
	   }

这里我们还可以对空间进行优化,我们可以观察到,f[i][…]只会用到f[i-1][…]的值,所以数组的第一维的空间完全可以用滚动数组的方式处理掉,但是如何不影响状态转移呢,我们来看滚掉之后的状态转移方程, f [ j ] = m a x ( f [ j ] , f [ j − v [ i ] [ k ] ] + w [ i ] [ k ] ) f[j] = max(f[j],f[j-v[i][k]]+w[i][k]) f[j]=max(f[j],f[jv[i][k]]+w[i][k]),这里的max里面的 f [ j ] 和 f [ j − v [ i ] [ k ] ] f[j]和f[j-v[i][k]] f[j]f[jv[i][k]]其实是 f [ i − 1 ] [ j ] 和 f [ i − 1 ] [ j − v [ i ] [ k ] ] f[i-1][j]和f[i-1][j-v[i][k]] f[i1][j]f[i1][jv[i][k]],而不是 f [ i ] [ j ] 和 f [ i ] [ j − v [ i ] [ k ] ] f[i][j]和f[i][j-v[i][k]] f[i][j]f[i][jv[i][k]],所以我们需要对体积的遍历做一些修改,从大到小循环,如果还是从小到大循环的话,那么这里的 f [ j ] 和 f [ j − v [ i ] [ k ] ] f[j]和f[j-v[i][k]] f[j]f[jv[i][k]]的含义就有可能是 f [ i ] [ j ] 和 f [ i ] [ j − v [ i ] [ k ] ] f[i][j]和f[i][j-v[i][k]] f[i][j]f[i][jv[i][k]],而不是我们需要的 f [ i − 1 ] [ j ] 和 f [ i − 1 ] [ j − v [ i ] [ k ] ] f[i-1][j]和f[i-1][j-v[i][k]] f[i1][j]f[i1][jv[i][k]],可以模拟一下就明白了,只靠想的话有点抽象.

3.练习题

下面给大家一道经典分组背包的练习题(练手)

题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1712
AC代码:

#include<bits/stdc++.h>
using namespace std;
int w[105][105];
int f[105]={0};
int main()
{
	int n,m;
	while(cin>>n>>m)
	{
	if(!n&&!m) break;
	memset(f,0,sizeof(f));
	/*w[i][j]表示在i课程上花j天学习所获得的价值*/
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 	cin>>w[i][j];
	 
	for(int i=1;i<=n;i++)
	 for(int j=m;j>=0;j--)
	  for(int k=1;k<=m;k++)
	   if(j>=k)//剩余的背包容量j还够学习k天的话 
	   {
	   	  f[j] = max(f[j],f[j-k]+w[i][k]);
	   }
	   cout<<f[m]<<'\n';
    }
    return 0;
} 

依赖背包

1.定义

什么是依赖背包,顾名思义就是具有依赖属性,这种背包常见于树形结构上面,例如:一棵树有N个节点,每一个节点放有一个物品,这些物品有自己的体积和价值,但是如果你要选择v好节点的物品,那么必须先选择v的父亲节点上的物品(所谓的依赖关系),现在你有容里为M的背包,问你选择物品的最大权值和是多少.

2.讲解

我们这里设dp[u][j]表示以u为根节点,背包剩余容里为j能够选择的物品的最大权值和,那么可想而知dp[u][j]这个值一定是由子节点更新来的,状态转移方程如下,用到了滚动数组优化

/*这里i最小为v[u]因为你要选子节点的话,u这个节点必选,给u留空间*/
for(int i=m;i>=v[u];i--) 
 for(int k=0;k<=i-v[u];i++)
  f[u][i] = max(f[u][i],f[u][i-k]+f[s][k]);//s是u的子节点 

那么很显然答案就是f[root][m],root是我们的根节点

3.练习题

题目链接
https://www.luogu.com.cn/problem/P1273

这道题就是分组背包+依赖背包的结合体型,一道很经典的树形dp

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 3010;
struct Edge
{
	int v;
	int next;
	int w;
}edge[10000];
int head[Maxn];
int val[Maxn];
int f[Maxn][Maxn];//f[u][j]以u为根节点,选择j个用户赚取的最大价值 
int cnt = 0;
void build(int u,int v,int w)
{
	edge[++cnt].v = v;
	edge[cnt].w = w;
	edge[cnt].next = head[u];
	head[u] = cnt;
	return ;
}
int n,m;
int dfs(int u)
{
	/*说明是用户*/
	if(u>(n-m))
	{
		f[u][1] = val[u];
		return 1;//用户个数 
	}
	int sum = 0,now;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v = edge[i].v;
		now = dfs(v);//以v为根节点的子树有多少个用户 
		sum+=now;
		for(int j=sum;j>=0;j--)
		 for(int k=1;k<=now;k++)
		 {
		 	if(j>=k)
		 	f[u][j] = max(f[u][j],f[u][j-k]+f[v][k]-edge[i].w);//edge[i].w是u到v的花费 
		 }
	}
	return sum;
	
}
int main()
{
	memset(f,~0x7f,sizeof(f));
 	memset(head,-1,sizeof(head));
 	cin>>n>>m;
	for(int i=1;i<=n;i++) f[i][0] = 0;//选0个用户赚不到钱 
	
	for(int i=1;i<=(n-m);i++)
	{
		int k;
		cin>>k;
		for(int j=1;j<=k;j++)
		{
			int A,C;
			cin>>A>>C;
			build(i,A,C);
		}
	}
	
	for(int i=n-m+1;i<=n;i++) cin>>val[i];
	dfs(1);
	for(int i=m;i>=0;i--)
	{
		if(f[1][i]>=0)
		{
			cout<<i<<'\n';
			break;
		} 
	}
	return 0;
}

题目连接
https://www.luogu.com.cn/problem/P2014
这是一道基于分组背包的模板题

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 400;
struct Edge
{
	int v;
	int next;
}edge[Maxn];
int cnt = 0;
int head[Maxn];
int w[Maxn];
int sz[Maxn];
int f[Maxn][Maxn];
void dfs(int u)
{
	f[u][1] = w[u];
//    f[u][0] = 0;
	sz[u] = 1;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v =edge[i].v;
		dfs(v);
		sz[u]+=sz[v];
		for(int j=sz[u];j>=0;j--)
		 for(int k = 0;k<=sz[v];k++)
		 {
		 	/*j-k>=1,因为选儿子的话必须选父亲要给父亲留位置*/
		 	if(j>k) f[u][j] = max(f[u][j],f[u][j-k]+f[v][k]);
		 }
	}
	return ;
}
void build(int u,int v)
{
	edge[++cnt].v = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
	return ;
}
int main()
{
	memset(f,~0x7f,sizeof(f));
	memset(head,-1,sizeof(head));
	
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) f[i][0] = 0;
	for(int i=1;i<=n;i++)
	{
		int u;
		cin>>u>>w[i];
		build(u,i);
	}
	w[0] = 0;
	dfs(0);
	cout<<f[0][m+1]<<'\n';
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

背包问题----分组背包(超详细讲解) 的相关文章

  • linux查看进程所有子进程和线程

    线程是现代操作系统上进行并行执行的一个流行的编程方面的抽象概念 当一个程序内有多个线程被叉分出用以执行多个流时 xff0c 这些线程就会在它们之间共享特定的资源 xff08 如 xff0c 内存地址空间 打开的文件 xff09 xff0c
  • 一款二进制文件查看器

    由于使用的是Notepad 43 43 64位版本 xff0c 在网上找了很多二进制查看插件HexEditor dll要么是32位不兼容 xff0c 要么是出现除零的错误 xff08 以前找到过一次支持Notepad 43 43 64位版本
  • YOLO目标检测

    一 背景 基于深度学习技术的视觉目标检测近年去的长足发展 但仍然有许多方面问题需要优化 二 YOLO算法的特点 YOLO作为一种性能优异的通用目标检测系统 xff0c 为了保证检测的效率 xff0c 提出one stage的思想 xff0c
  • (Qt中添加编译选项)QT在交叉编译时出现parameter passing for argument of type ‘std::_Rb_tree xxxxx changed in GCC 7.1

    QT版本都是5 1x 先是在Ubuntu机器上写的代码 xff0c GCC版本为5 4 xff0c 代码编译无 任何警告 后来移植到开发板 xff08 GCC版本为7 1 xff09 进行编译时 xff0c 提示这种警告 发生在代码中对st
  • C++按行读取文本并解析

    项目中需要按行读取文本文件 xff0c 并对每一行内容进行解析 每一行都是固定的字段数 xff0c 字段之间用空格隔开 span class token macro property span class token directive k
  • error C2447: “{”: 缺少函数标题(是否是老式的形式表?)

    error C2447 缺少函数标题 是否是老式的形式表 网上有人说 这个BUG是因为在win7上使用了 LF 的格式编码导致的 使用Notepad 43 43 修改成 BOM UTF8 和 windows 的 CR LF 格式一切正常 确
  • Visual Studio 2017 代码自动对齐

    点 编辑 高级 设置选定内容的格式 或者按Ctrl 43 K 然后再按Ctrl 43 F 就好了 你可以在常用快捷键自定义 窗口中进行查看 1 进入工具 选项 对话框 2 选择 环境 键盘 3 在 显示命令包含 下面的对话框中输入 对齐 关
  • CSDN 排版之颜色、字体、字号及背景色

    颜色 xff1a span class token operator lt span font color span class token operator 61 span blue span class token operator g
  • 使用QTCreator编程时,如何利用dmp文件定位程序奔溃

    写这篇文章之前 xff0c 看了一些其他人的博客 xff0c 但不是很详细 xff0c 缺这少那的 xff0c 好多都是复制粘贴别人的东西 自己动手弄了弄 xff0c 可以使用 xff0c 就记下来备忘与分享 前言 开发环境说明 编程IDE
  • Pytorch中transforms.RandomResizedCrop使用说明

    加载数据 训练集数据扩充 数据增强 和归一化 数据扩充 数据增强 的意义在于通过人为的随机范围裁剪 缩放 旋转等操作 增大训练集中的数据多样性 全面性 进而提高模型的泛化能力 训练集数据扩充和归一化 在验证集上仅需要归一化 data tra
  • C++中调用Python的办法

    1 背景 一直采用C 43 43 作为主语言开发 xff0c 最近遇到一个项目需要解析PDF文件中的文本内容 xff0c 直接采用C 43 43 来做显得不是很方便 xff0c 但用python来做就显得很简单了 难点在于如何C 43 43
  • Qt Creator远程调试嵌入式ARM开发板

    1 环境 Win10 64位系统上通过Virtual Box安装了一个Ubuntu虚拟机 ubuntu的版本 xff1a Linux kernel 4 15 0 142 generic 146 16 04 1 Ubuntu SMP Ubun
  • 套接字(描述符)读取指定的字节数

    检测fd句柄是否可读 xff0c ms毫秒超时 参数 xff1a df in 检测的句柄 ms in 超时 xff0c 毫秒 返回 xff1a 1 可读 xff0c 或者已经断开 0 超时 xff0c 仍然不可读 1 错误 int IsRe
  • 4.4线索二叉树遍历

    1 中序线索二叉树遍历 找到第一个中序遍历的结点 ThreadNode span class token operator span span class token function Firstnode span span class t
  • 自动根据本机字节序 将小端字节序的报文(字符数组)转为整数

    1 xff0c 判断本机的字节序 xff08 大端优先 小端优先 xff09 判断当前PC为大端还是小端字节序 64 返回值 xff1a 1 大端 xff1b 0 小端 int JudgeEndianOfPC int num 61 1 if
  • 智能指针的使用

    智能指针在C 43 43 11版本之后提供 xff0c 包含在头文件 lt memory gt 中 xff0c shared ptr unique ptr weak ptr 1 xff0c shared ptr的使用 shared ptr使
  • 图像检索系列——利用深度学习实现以图搜图

    转载自 xff1a 图像检索系列 利用深度学习实现以图搜图 知乎 前言 在上一篇文章 图像检索系列 利用 Python 检测图像相似度 中 xff0c 我们介绍了一个在图像检索领域非常常用的算法 感知哈希算法 这是一个很简单且快速的算法 x
  • Windows下select模型(以及EAGAIN、EWOULDBLOCK、EINTR)

    在这里记录一下 xff0c 以前都是新项目用到了就从旧项目中拷贝 自从将博客当作记事本 xff0c 发现自己多了一个好习惯 Windows下select模型 程序员攻略 CSDN博客 套接字IO超时设置和使用select实现超时管理 wj
  • RANSAC算法理解

    RANSAC是 RANdom SAmple Consensus xff08 随机抽样一致 xff09 的缩写 它可以从一组包含 局外点 的观测数据集中 xff0c 通过迭代方式估计数学模型的参数 它是一种不确定的算法 它有一定的概率得出一个
  • C++ 环形缓冲区(队列)简单实现

    1 说明 在实际工作中 xff0c 如果数据流量过大 xff0c 可以先把数据接收到数据缓冲区中 xff0c 处理之后再取出 我们定义的包协议可以采用定长包 xff0c 可以采用不定长度的包 xff0c 环形缓冲区都能处理 2 使用场景 2

随机推荐

  • Visual Studio Code (vscode) 配置 C / C++ 环境

    Visual Studio Code vscode 配置 C C 43 43 环境 步平凡 博客园 在电脑安装软件管控严格的情况下 xff0c 想装VS装不了 xff0c 就装轻量版的VSCode了 以上写得很好 xff0c 照做即可 本人
  • c++实现basename

    window API居然不包含Linux中很好用的basename函数 xff0c 实现了一下 xff0c 留个记录 xff0c 省得日后重复写 std string m basename std string fullPath size
  • tortoiseGit教程

    0 前言 TortoiseGit其实是一款开源的git的版本控制系统 xff0c 也叫海龟git TortoiseGit提供了人性化的图形化界面 xff0c 不用像Git一样输入许多语句 xff0c 像git init git add gi
  • 用STL库创建线程

    测试了3种方式 xff1a 1 xff1a 子线程不带返回值 2 xff1a 子线程带返回值 3 xff1a 子线程带引用类型参数 使用join方式 xff0c 让父线程等待子线程运行结束 TestTemp cpp 定义控制台应用程序的入口
  • 4.5树的存储

    双亲表示法 xff0c 孩子表示法 xff0c 孩子兄弟表示法 1 双亲表示法 查找双亲简单 空数据导致遍历更慢 xff0c 查指定节点的孩子只能遍历 span class token keyword typedef span ElemTy
  • Windows下MySQL数据库的安装、配置及C++使用案例

    1 安装及配置 Windows判断本地是否安装mysql以及mysql安装过程 企鹅要去银河思考人生 xff01 xff01 xff01 的博客 CSDN博客 windows查看是否安装mysql 注意按照文中提示 xff0c 配置好环境变
  • C++获取系统毫秒级时间(自1970年1月1日至今的毫秒数)

    跟系统时间相关的 ifdef WIN32 include lt time h gt include lt windows h gt else include lt sys time h gt endif unsigned long long
  • Window 10下SQL Server的安装配置以及C++使用案例

    1 SQL Server2008的安装与配置 参照下面这篇博客实现即可 里面提供了安装包下载方式 xff08 百度网盘有点慢 xff09 安装及配置步骤 SQLServer安装教程 xff08 史上最详细版本 xff09 杨林伟的博客 CS
  • 基于OpenCV实现的多角度、多目标模板匹配项目实战案例

    1 说明 本案例采用NCC的匹配 金字塔 为了加速 思想 基于OpenCV实现的多角度 多目标模板匹配 不支持尺度不变 若研究旋转 尺度不变性的匹配 请参考本人的OpenCV专栏内的 nbsp OpenCV实现多角度多尺度模板匹配 基于形状
  • 程序日志模块的两种模式

    程序员都知道程序的运行日志在不少时候都非常有用 xff0c 利于排查 理清逻辑 一般而言 xff0c 日志都按天生成 xff0c 并且具备自动清理多少天以前的旧日志 xff0c 避免无限增长占用磁盘 下图展示了2种日志模式 模式一 1 xf
  • C++开发面试常考

    C 43 43 后台开发面试常考 pudn com
  • 如何在微软官网上下载旧版本的visual studio

    想在微软官网下载旧版本的VS 太长不想看的可以直接戳网址进入最终的界面 xff1a Visual Studio 较旧的下载 2019 2017 2015 和以前的版本想从官网首页一步一步进入到最终下载界面的可以看下面详细步骤 xff1a 1
  • 基于OpenCV实现的RANSAC随机抽样一致性直线拟合

    概要 本文介绍基于ransac随机抽样一致性随机抽样一致性的直线拟合方法 涵盖一下的内容 ransac的算法思想 ransac的算法步骤 如何调整ransac算法迭代的次数 基于opencv编码实现 ransac算法流程 RANSACRAN
  • 基于OpenCV(C++)实现的RANSAC随机抽样一致性的曲线拟合(二次)

    nbsp 0 前言 nbsp nbsp 前不久整理了RANSAC直线拟合的文章 基于OpenCV实现的RANSAC随机抽样一致性直线拟合 thequitesunshine007的博客 CSDN博客 这篇文章与其类似 只是从拟合直线变为拟合曲
  • 最小二乘least-squares拟合曲线(三次或多次)

    1 说明 基于最小least squares去拟合出多次曲线 考虑到了所有的样本点 因此这种方法对噪声敏感 尤其是遇到较为突兀明显的噪声时 曲线的形状易受干扰 2 代码 代码细节仔细读基本都能读懂 或者查一下也不是什么大问题 include
  • 4.6树和森林的遍历

    一 树的遍历 1 先根遍历 对应二叉树先序遍历 span class token keyword void span span class token function PreOrder span span class token punc
  • 扩展欧几里得算法(简单易懂,详细分析)

    扩展欧几里得算法 扩展欧几里得算法证明 43 应用 61 61 欧几里得算法 61 61 61 61 扩展欧几里得算法 61 61 应用1 xff1a 求一元二次线性方程的整数解应用二 xff1a 求ax 61 c mod p 应用三 xf
  • 佩尔方程(超详细推导+例题讲解) 每日一遍,算法再见!

    这里写目录标题 佩尔方程第一类佩尔方程第一类佩尔方程例题讲解 第二类佩尔方程 佩尔方程 第一类佩尔方程 定义 xff1a 形如 x 2 d
  • FFT(傅里叶快速变换,详细讲解+推导) 每日一遍,算法再见!

    FFT详细推导 FFT 傅里叶快速变换 一 前置知识1 复数和单位根2 单位根的三个引理3 多项式 二 FFT 快速傅里叶变换推导 三 IFFT四 FFT求解多项式乘积模板代码1 递归版2 非递归版 这个更快 xff0c 省去了递归时间 五
  • 背包问题----分组背包(超详细讲解)

    树形dp 分组背包 43 依赖背包 分组背包1 定义2 讲解3 练习题 依赖背包1 定义2 讲解3 练习题 分组背包 1 定义 分组背包 xff0c 通俗的讲就是 xff0c 给你N组物品 xff0c 然后每一组你至多选择一个物品 也可以不