背包问题学习

背包问题是常见的动态规划dp的问题

下面用到的符号:

  • 常用n表示物品数, m表示背包容积
  • f[i][j]表示i件物品, j的背包容量的最大价值
  • w[i]表示第i件物品的价值, v[i] 表示第i件物品的容量
  • f[0][0~m] = 0, 所以n可以从1开始遍历
  • 一般是有两层嵌套循环 第一层遍历物品, 第二层遍历背包容量, 第三层视情况, 若完全背包or多重背包 需要遍历决策 判断 k*v[i] <= j (背包容量)

0/1背包

下图是0/1背包的分析:
0/1背包问题的分析

题目 (0/1背包)

在这里插入图片描述

输入输出

输入输出

核心代码

for(int i = 1; i <= n; i++)for(int j = 0; j <= m; j++){ // 不难发现 f[i][] 都是依赖于 f[i-1][] 的f[i][j] = f[i-1][j];if(v[i]<=j) f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]);    
}

优化: 降维 二维 -> 一维

需要注意的是在遍历背包容量的时候, 需要逆序遍历背包容量, 因为物品只能取一个和不取两种状态, 而逆序能够保证这个。

完整代码如下:

#include<iostream>
#include<algorithm>using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N]; // 物品体积 价值
int f[N]; int main(){scanf("%d%d",&n,&m);for(int i = 1; i<=n; i++) scanf("%d%d",&v[i],&w[i]);for(int i = 1; i<=n;i++){for(int j = m; j>=v[i]; j--){  // 每个物品只能使用一次 (逆序)f[j] = max(f[j], f[j-v[i]]+w[i]);}}printf("%d\n",f[m]);return 0;
}

完全背包

下图是完全背包的分析:

完全背包分析

题目(完全背包 物品可以无限拿, 只要背包容量还能放得下)
题目和输入/输出
题目输入和输出

朴素做法(三层循环)

#include<iostream>
#include<algorithm>using namespace std;const int N = 1010;int n, m;
int v[N], w[N];
int f[N][N];int main(){cin>>n>>m;for(int i = 1; i <= n; i++) scanf("%d%d",&v[i],&w[i]);for(int i = 1; i<=n; i++){ // 列举物品体积for(int j = 0; j<=m; j++){  // 列举背包for(int k = 0; k*v[i] <= j; k++)// 决策 (背包体积有限)f[i][j] = max(f[i][j], f[i-1][j-k*v[i]]+w[i]*k);}}cout<<f[n][m]<<endl;return 0;
}

优化:

(通过列举 f[i][j] 以及 f[i][j-v]两种情况, 发现有很多相同之处, 只相差个v) => 化简得

f[i]][j] = max(f[i-1][j], f[i-1][j-v]+w) 两种状态, 一个是取第i件物品, 另一个是不取第i件物品

 for(int i = 1; i <= n; i++){ // 列举物品for(int j = 0; j <= m; j++){ // 背包体积f[i][j] = f[i-1][j];if(j >= v[i]) f[i][j] = max(f[i][j], f[i][j-v[i]]+w[i]);}}

其实上述代码与0/1背包的代码有很多雷同的地方(都是两种状态, 要么取第i件, 要么不取), 只不过 0/1 背包是 f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]), 而完全背包是 f[i][j] = max(f[i][j], f[i][j-v[i]]+w[i]) (前一部分是不取第i件, 后面是取第i件的方案)
这个状态转移方程的含义可以这样理解:

  1. 不取第 i 件物品:这种情况下,背包的最大价值保持不变,即为 f[i][j]。这实际上是上一个状态的值,因为我们没有添加新的物品。
  2. 取第 i 件物品:如果我们决定把第 i 件物品放入背包中,那么背包的剩余容量变为 j-v[i]。因此,当前的总价值是放入这件物品之前的总价值 f[i][j-v[i]] 加上这件物品的价值 w[i]

对于上面代码的转化:

for(int i = 1; i<=n; i++){for(int j = v[i]; j <= m; j++){f[i][j] = max(f[i-1][j], f[i][j-v[i]]+w[i]);}
}

继续优化:

2维压缩成1维: 这里因为完全背包问题, 物品可以取无数次, 所以第二层循环循环遍历背包容量时, 顺序遍历

for(int i = 1; i<=n; i++){for(int j = v[i]; j <= m; j++){f[j] = max(f[j], f[j-v[i]]+w[i]);}
}
cout<<f[m]<<endl;

多重背包

与上面的背包问题不同的是每个物品数量有上限: 0~s[i]
题目:
题目输入输出

状态划分: 两种 -> 取第i件物品 / 不取第i件物品

朴素做法

状态方程:f[i][j] = max(f[i-1][j-k*v[i]]+w[i]*k, f[i][j]), k = 0, 1, 2... s[i]

初级版 多重背包

 #include<iostream>#include<algorithm>using namespace std;const int N = 110;int n, m;int s[N], v[N], w[N];int f[N][N]; // 1~N 个物品, 背包容量为N 所能达到的最大价值 (max)int main(){scanf("%d%d", &n,&m);for(int i = 1; i<=n; i++){ // 输入第i个物品的体积、价值和重量scanf("%d%d%d",&v[i],&w[i],&s[i]);}for(int i = 1; i <= n; i++){ // 物品for(int j = 0; j <= m; j++){ // 背包体积for(int k = 0; k <= s[i] && k*v[i] <= j; k++)f[i][j] = max(f[i-1][j], f[i][j-k*v[i]]+w[i]*k);}}printf("%d\n",f[n][m]);return 0;}

当数据量比较大的时候, 上面的代码会出现 TLE, 因此我们需要对上述代码进行优化
优化:

困难版 多重背包
我们可以尝试展开f, 尝试寻找规律

f[i][j] = max(f[i-1][j], f[i-1][j-v]+w,...,f[i-1][j-sv]+sw)

f[i][j-v] = max( f[i-1][j-v], f[i-1][j-2v]+w,...+f[i-1][j-sv]+(s-1)w, f[i-1][j-s+1]v+sw)

但是从上式找不到其中规律,所以下述使用二进制拆解第i个物品个数s[i] 去优化上述代码

2^n的方式进行拆解包-> 01背包问题 s->logs 组, 且每组只能选一次 (拆分)

 #include<iostream>#include<algorithm>using namespace std;const int N = 25000, M = 2010; // 物品数量N NlogS 1000*log2000, 背包体积 Mint n, m;int v[N], w[N]; // 物体体积, 价值int f[N]; // dp数组, 这里使用一维数组, 因为物品数量进行重组了/*多重背包的优化 (二进制对物品数量进行拆分)对物品进行拆分, 这样就少了一层循环(第三层k)*/int main(){cin>>n>>m;int cnt = 0;for(int i = 1; i <= n; i++){int a, b, s;cin>>a>>b>>s; // 输入 第i种物品的体积, 价值int k = 1;while(k <= s){ // 拆分次数 s[i]!cnt++;v[cnt] = a * k; // k个第i件物品体积 k个 第i件物品体积为aw[cnt] = b * k;  // 更新第i个物品的价值s -= k;k *= 2;}if(s > 0){ // 剩余的(s个物品)为一件cnt++;v[cnt] = a*s; w[cnt] = b*s;}}n = cnt; // 更新物品数量// 后继转为 0/1 背包问题, 每个物品 要么拿一次, 要么不拿for(int i = 1; i<=n; i++){for(int j = m; j >= v[i]; j--){f[j] = max(f[j], f[j-v[i]]+w[i]);}}cout<<f[m]<<endl;return 0;}

上述介绍了常见的三种背包问题的思路和基础代码模板以及相应的优化。希望各位大佬能够给予指导以及相应的意见, 后续时间也会取更新更多更丰富的算法内容, 谢谢大家的观看♥~

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

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

相关文章

无线4G电表和有线智能电表哪个通讯更稳定?

当谈论智能电表的通讯方式时&#xff0c;常常会提到无线4G电表和有线智能电表两种选择。那么&#xff0c;这两款电表哪个通讯更稳定呢&#xff1f;下面&#xff0c;我们就一起来看下吧&#xff01; 首先&#xff0c;我们来看无线4G电表的通讯方式。无线4G电表通过无线网络进行通…

日志收集 grafana-loki

文章目录 部署 grafana-loki部署 grafana配置 loki 源配置节点大盘 部署 grafana-loki 官方文档&#xff1a;部署 grafana-loki 部署命令 设置集群的存储类&#xff0c;如果有默认可以不设置设置命名空间 helm install loki oci://registry-1.docker.io/bitnamicharts/grafa…

Android 缩减、混淆处理和优化应用

为了尽可能减小应用的大小&#xff0c;您应在发布 build 中启用缩减功能来移除不使用的代码和资源。启用缩减功能后&#xff0c;您还会受益于两项功能&#xff0c;一项是混淆处理功能&#xff0c;该功能会缩短应用的类和成员的名称&#xff1b;另一项是优化功能&#xff0c;该功…

学生档案管理系统设计

摘要 随着科学技术的不断提高,计算机科学日渐成熟,其强大的功能已为人们深刻认识,它已进入人类社会的各个领域并发挥着越来越重要的作用。作为计算机应用的一部分,使用计算机对学生档案信息进行管理,具有着手工管理所无法比拟的优点.例如:检索迅速、查找方便、可靠性高、存储量…

Vue3-数据交互请求工具设计

1.安装axios pnpm add axios 2.利用axios.create创建一个自定义的axios来使用 参考官网&#xff1a;axios中文文档|axios中文网 | axios 在src/utils文件夹下新建request.js&#xff0c;封装axios模块 import axios from axios const baseURL const instance axios.creat…

你真的掌握结构体了么?结构体习题(C语言)

前言 上一期博客我们学习了结构体的相关知识&#xff08;上期链接&#xff09;&#xff0c;但是学了不练也是不行的&#xff0c;我们今天讲给大家分享两道有点恶心的题目&#xff0c;让大家来加深对结构体的理解&#xff0c;那么话不多说我们现在开始吧&#xff01; 第一题 有…

2023年最详细介绍Linux 系统目录结构!你确定不来了解一下吗?

&#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Linux》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有…

网络和Linux网络_11(数据链路层)以太网(MAC帧)协议+局域网转发+ARP协议

目录 1. 以太网协议 1.1 MAC地址 1.2 以太网帧格式 2. 局域网转发原理 2.1 数据碰撞和交换机 2.2 最大传输单元MTU 3. ARP协议 3.1 ARP协议格式 3.2 模拟APR协议工作过程 3.3 ARP缓存表 4. 重看TCP/IP四层模型 本篇完。 1. 以太网(MAC帧)协议 网络层的IP协议并不是…

Linux基础命令(测试相关)

软件测试相关linux基础命令笔记 操作系统 常见Linux&#xff1a; Redhat系列&#xff1a;RHSL、Centos、FedoraDebian系列&#xff1a;Debian、Ubuntu以上操作系统都是在原生Linux系统上&#xff0c;增加了一些软件或功能。linux的文件及路径特点 Linux没有盘符的概念&#xf…

PAD平板签约投屏软件要如何选

又是一年年底了&#xff0c;年会开始多起来了&#xff0c;许多会务公司或活动公司会接到很多平板签约投屏业务&#xff0c;如年会中的签军令状、业绩保证书等。这时就面临选购一套签约投屏软件了。 目前的签约投屏软件&#xff0c;大多以H5做的网页版的多&#xff0c;但我建议…

ModbusRTU\TCP消息帧解析(C#实现报文发送与解析)

目录 知识点常用链接一、Modbus1.ModbusRTU消息帧解析2.主站poll、从站slave通讯仿真-modbusRTU1.功能码01读线圈状态2.功能码03读保持寄存器报文解析&#xff08;寄存器存整型&#xff09;报文解析&#xff08;寄存器存float&#xff09; 3.C#模拟主站Poll&#xff08;ModbusR…

Redis 入门、基础。(五种基本类型使用场景)

文章目录 1. 概况1.1 认识 NoSQL1.1.1 查询方式1.1.2 事务1.1.3 总结 2. 认识 Redis4. Redis 常见命令4.1 Redis 数据结构介绍4.2 Redis 通用命令4.3 Redis 命令之 String 命令4.4 Redis 命令的层级结构4.5 Redis 命令之 Hash 命令4.6 Redis 命令之 List 命令4.7 set 唯一不排序…

DOM 事件的注册和移除

前端面试大全DOM 事件的注册和移除 &#x1f31f;经典真题 &#x1f31f;DOM 注册事件 HTML 元素中注册事件 DOM0 级方式注册事件 DOM2 级方式注册事件 &#x1f31f;DOM 移除事件 &#x1f31f;真题解答 &#x1f31f;总结 &#x1f31f;经典真题 总结一下 DOM 中如何…

Linux简单部署Yearning并结合内网穿透工具发布至公网可访问

目录 前言 1. Linux 部署Yearning 2. 本地访问Yearning 3. Linux 安装cpolar 4. 配置Yearning公网访问地址 5. 公网远程访问Yearning管理界面 6. 固定Yearning公网地址 前言 Yearning 简单, 高效的MYSQL 审计平台 一款MYSQL SQL语句/查询审计工具&#xff0c;为DBA与开发…

深度学习之图像分类(十五)DINAT: Dilated Neighborhood Attention Transformer详解(一)

Dilated Neighborhood Attention Transformer Abstract Transformers 迅速成为跨模态、领域和任务中应用最广泛的深度学习架构之一。在视觉领域&#xff0c;除了对普通Transformer的持续努力外&#xff0c;分层Transformer也因其性能和易于集成到现有框架中而受到重视。这些模…

Domino多Web站点托管

大家好&#xff0c;才是真的好。 看到一篇文档&#xff0c;大概讲述的是他在家里架了一台Domino服务器&#xff0c;上面跑了好几个Internet的Web网站&#xff08;使用Internet站点&#xff09;。再租了一台云服务器&#xff0c;上面安装Nginx做了反向代理&#xff0c;代理访问…

GPTS-生成一个动漫图像GPT

介绍 GPTs是ChatGPT的定制版本,用户可以通过组合指令、知识和功能来定制用于特定任务或主题的GPT。它们可以根据需要简单或复杂,解决从语言学习到技术支持等各种事情。 创建GPTs Plus和Enterprise用户可以在chat.openai.com/create上开始创建GPTs。 您可以通过在ChatGPT上的…

【头歌系统数据库实验】实验4 MySQL单表查询

目录 第1关. 在users表中新增一个用户&#xff0c;user_id为2019100904学号&#xff0c;name为2019-物联网-李明 第2关. 在users表中更新用户 user_id为robot_2 的信息&#xff0c;name设为 机器人二号 第3关. 将solution表中所有 problem_id 为1003 题目的解答结果&#xf…

车联网架构设计(二)_消息缓存

在上一篇博客车联网架构设计(一)_消息平台的搭建-CSDN博客中&#xff0c;我介绍了车联网平台需要实现的一些功能&#xff0c;并介绍了如何用EMQXHAPROXY来搭建一个MQTT消息平台。车联网平台的应用需要消费车辆发布的消息&#xff0c;同时也会下发消息给车辆&#xff0c;以实现车…

UE4/UE5 材质实现带框环形进度条

UE4/UE5 材质实现带框环形进度条 此处使用版本&#xff1a;UE4.27 原理&#xff1a;大圆减小圆可以得到圆环&#xff0c;大圆环减小圆环&#xff0c;可以得到圆环外围线框 实现效果&#xff1a; 实现&#xff08;为了给大家放进一张面前能看的图&#xff0c;我费劲了心思&…