背包问题汇总(01背包、完全背包、多重背包、分组背包)

背包问题

  • 01 背包
  • 完全背包
  • 多重背包
  • 分组背包

01 背包

有 n 件物品,每个物品只能使用一次,在不超过背包体积的情况下,总价值最大是多少?

#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main() // 优化前
{cin >> n >> m;for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];for(int i = 1; i <= n; i++) // 选第 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 - 1][j - v[i]] + w[i]);}}cout << f[n][m] << endl;return 0;
}

状态表示可以优化为一维数组!

f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]); 这里求 f[i][j] 使用的是 f[i-1] 层的东西,所以在优化为一维后,需要从后往前遍历 j ,这样可以确保后面使用的 f[i - 1] 是上一层未被更新的,而不是更新后的!

优化后:

#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{cin >> n >> m;for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];for(int i = 1; i <= n; i++) // 选第 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;
}

完全背包

每个物品可以拿无数个,求不超过背包体积的情况下,总价值最大是多少

暴力写法:可以直接采用 拿与不拿 的思想,第 i 个物品最多可以拿 k 个,在枚举物品种类和价值的基础下,再枚举这 k 种情况即可

#include <iostream>
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++) cin >> 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]] + k * w[i]);cout << f[n][m];return 0;
}

使用数学方法经化简可以得出:f[i][j] 的最大值恰好比 f[i][j - v[i]] 多了 w[i],那么 f[i][j] 的状态转移方程就可以优化为:f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i]);,这样,就直接可以使用一维背包的套路来做了:

#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{cin >> n >> m;for(int i = 1; i <= n; i++) cin >> v[i] >> w[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]);}}cout << f[n][m];return 0;
}

优化为一维:但完全背包的 j 是从 v[i] 开始,往大的枚举,这个与 01 背包恰好相反,其他的一维优化后与 01 背包别无二致!

#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{cin >> n >> m;for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];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];return 0;
}

多重背包

有 N 种物品和一个容量是 V 的背包。第 i种物品最多有 s[i] 件,每件体积是 v[i],价值是 w[i],求不超过背包容量的情况下,总价值最大时多少?

暴力解法,在枚举物品种类和体积的基础上,依次枚举符合符合条件的物品个数(k <= s[i] && k * v[i] < j)

时间复杂度:O(n* m * s)

#include <iostream>
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N][N];
int main()
{cin >> n >> m;for(int i = 1; i <= n; i++) cin >> 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][j], f[i - 1][j - k * v[i]] + k * w[i]);cout << f[n][m] << endl;return 0;
}

优化:二进制优化

每个物品可选择的个数 s[i],都能通过 多个2^x + 某一个数 来表示出来,比如,76,就可以用 1 + 2 + 4 + 8 + 16 + 32 + 13 来表示,那么枚举 s[i] 个第 i 个体积为 w[i] 的数,就转化为了枚举:
一个体积为 v[i],价值为w[i]的物品, 一个体积为 2 * v[i],价值为2 * w[i]的物品,一个体积为 4 * v[i],价值为4 * w[i]的物品 …,一个体积为 k * v[i],价值为 w[i] 的物品。这样,时间复杂度就被优化为了 :O(n* m* logs)

最后就转化为了:cnt 个物品的 01 背包问题!

#include <iostream>
using namespace std;
// s[i] 2000 可以拆分为 log2000 ~= 11 个物品, 所以开 12000 个空间足够 
const int N = 12000;
int n, m;
int v[N], w[N];
int f[N];
int main()
{cin >> n >> m;int cnt = 0; // 记录优化后的物品编号for(int i = 1; i <= n; i++){int a, b, s;cin >> a >> b >> s;int k = 1;while(k <= s){cnt ++;v[cnt] = k * a;w[cnt] = k * b;s -= k;k *= 2;}if(s > 0) // 剩余的不能使用 2^ x 次方表示的{cnt ++;v[cnt] = s * a;w[cnt] = s * b;}}n = cnt;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;
}

分组背包

有 N 组物品和一个容量是 V 的背包,每组物品有若干个,同组内的物品最多只能选一个。

利用 dp 中选或不选的思路,枚举 k + 1 种情况:不选第 i 种物品;选第一个;选第二个;选第三个…

三重循环枚举,时间复杂度:O(N^3)

代码易错点:ks[] 是从 0 开始还是从1开始必须想好,更新f[i][j] = f[i - 1][j];,必须放在第三重循环外,第二重循环内!

#include <iostream>
using namespace std;
const int N = 110;
int v[N][N], w[N][N], s[N];
int m, n;
int f[N][N];
int main()
{cin >> n >> m;for(int i = 1; i <= n; i++){cin >> s[i]; // 输入第 i 种物品的个数for(int j = 1; j <= s[i]; j++)cin >> v[i][j] >> w[i][j]; // 输入第 i 种物品的 k 个体积和价值 }for(int i = 1; i <= n; i++) // 枚举物品种类 {for(int j = 0; j <= m; j++) // 枚举体积{f[i][j] = f[i - 1][j]; // 必须放外面! 因为这是不选的情况 for(int k = 1; k <= s[i]; k++) // 枚举第 i 个数的 k 种取法if(j >= v[i][k]) f[i][j] = max(f[i][j], f[i - 1][j - v[i][k]] + w[i][k]);}}cout << f[n][m] << endl;return 0;
}

优化为一维:

#include <iostream>
using namespace std;
const int N = 110;
int v[N][N], w[N][N], s[N];
int m, n;
int f[N];
int main()
{cin >> n >> m;for(int i = 1; i <= n; i++){cin >> s[i]; // 输入第 i 种物品的个数for(int j = 1; j <= s[i]; j++)cin >> v[i][j] >> w[i][j]; // 输入第 i 种物品的 k 个体积和价值 }for(int i = 1; i <= n; i++) // 枚举物品种类 {for(int j = m; j >= 0; j--) // 因为要用上一层的状态,因此必须从大到小 {for(int k = 1; k <= s[i]; k++) // 枚举第 i 个数的 k 种取法if(j >= v[i][k])  // 说明可以选 f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);}}cout << f[m] << endl;return 0;
}

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

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

相关文章

C语言-01_HelloWord

文章目录 1.C程序运行机制2.HelloWorld的剖析① main()② 函数体③ printf()④ 标准库、头文件 3.输出3.1 printf()标准格式3.2 占位符3.3 输出格式 1.C程序运行机制 过程1&#xff1a;编辑 编写C语言源程序代码&#xff0c;并已文件的形式存储到磁盘中。源程序文件以“.c”作…

Java复数计算

复数在数学、科学或者工程领域是很常用的&#xff0c;可以通过调用Apache Commons Math库来完成&#xff0c;也可以自己手撸。 一、使用Apache Commons Math库 这个库有多个版本&#xff0c;在写这篇文章时&#xff0c;它的最新版是2022年12月19日的4.0-beta1&#xff0c;构建…

k8s自定义资源你会创建吗

创建自定义资源定义 CustomResourceDefinition 当你创建新的 CustomResourceDefinition&#xff08;CRD&#xff09;时&#xff0c;Kubernetes API 服务器会为你所 指定的每一个版本生成一个 RESTful 的 资源路径。CRD 可以是名字空间作用域的&#xff0c;也可以是集群作用域的…

并发——线程

为了并发的执行任务(程序)&#xff0c;现代操作系统特地引入了“进程”的概念 分析&#xff1a; 1. 进程的地址空间是独立的&#xff0c;进程间通信的代价比较大 如果进程需要进行数据的交换&#xff0c;则需要用到进程间通信(pipe / fifo / shm / msg / .…

VHDL/CPLD硬件描述语言:2022年做的万年历实验

之前接触过一些硬件描述语言以及VHDL/CPLD的单片机的设计实验&#xff0c;那时是2022年了 这里补写一篇笔记,以记录一下那十多个小时 万年历实验 研究中的心得体会&#xff1a; 说明解释都是个人理解&#xff0c;与标准描述有较大出入...... 目录 输入输出器件的编写: 分频器…

HTML5 Web Workers 详解 (2)

三、高级使用 1. 多个 Web Workers 你可以在主线程中创建多个 Web Workers 来处理并行任务。例如&#xff0c;下面的代码创建了两个 Worker 并分别处理不同的任务。 主线程代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"…

8086 汇编笔记(五):包含多个段的程序

一、在代码段中使用数据 “dw”的含义是定义字型数据 dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h code segmentdw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987hmov bx,0mov ax,0mov cx&#xff0c;8s: add ax cs:[bx]add bx,2loop smov ax,4c00hint 21hcode…

MVC前端定义变量的艺术:深入解析与实战策略

MVC前端定义变量的艺术&#xff1a;深入解析与实战策略 在MVC&#xff08;Model-View-Controller&#xff09;架构中&#xff0c;前端定义变量是一个至关重要的环节。它涉及到数据的存储、传递和展示&#xff0c;直接影响着应用程序的性能和用户体验。本文将从四个方面、五个方…

HBase 常用 shell 操作

下面给大家介绍一些HBase 常用 shell 操作&#xff0c;各位看官看好了啦&#xff0c;我要献丑了。 进入 HBase 客户端命令操作界面 $ bin/hbase shell查看帮助命令 > help查看当前数据库中有哪些表 > list创建一张表 创建 user 表&#xff0c;包含 info、data 两个列…

【Keil 5】Keil 5下载安装激活到2032年(含MDK、C51、STM32单片机)+附带百度网盘链接

这里写目录标题 安装包、激活文件下载1.双击mdk 514开始安装2.一路点next&#xff0c;信息随便写即可3.激活4.安装STM325.激活c51 安装包、激活文件下载 解压密码&#xff1a;lantongxue 链接&#xff1a;https://pan.baidu.com/s/15Aukt0j1HCFyHBE6whuDeg?pwdsjyh 提取码&…

Streamsets-JDBC模式使用更新时间字段数据同步

StreamSets的开源地址&#xff1a;https://github.com/streamsets/datacollector-oss Streamsets官网地址&#xff1a;https://streamsets.com/ Streamsets文档地址&#xff1a;https://docs.streamsets.com/portal/datacollector/3.16.x/help/index.html 我又来写Streamsets了…

LangChain实战技巧之四:当模型(Model)不支持Tool/Function的解决办法

文心大模型两大主力模型已全面免费&#xff0c;可参考我之前发的文章 AI菜鸟向前飞 — 今日三则AI相关新闻 但是&#xff0c;这些模型原生并不支持Tool/Function Call 如下所示&#xff1a; tool def greeting(name: str):向朋友致欢迎语return f"你好啊, {name}"…

基于LabVIEW虚拟示波器设计

随着计算机技术的发展&#xff0c;传统仪器开始向计算机化的方向发展。虚拟仪器是90年代提出的新概念。虚拟仪器技术的提出与发展&#xff0c;标志着二十一世纪自动测试与电子测量仪器领域技术发展的一个重要方向。所谓虚拟仪器&#xff0c;就是在通用的计算机平台上定义和设计…

ROS2自定义服务接口

ROS2自定义服务接口 在src/village_interface 下构建srv文件夹 src/village_interface/srv 下新建一个BorrowMoney.srv 遵循大小写编程规范 # 客户端请求 string name uint32 money # 中间这三个横杠很重要 不能删掉 --- # 服务端响应 bool success uint32 money接口编译 修改…

TDR原理的介绍

目录 简介 简单定义 TDR测试原理 简介 时域和频域就像孪生兄弟一样&#xff0c;经常在测试测量领域同时出现&#xff0c;可谓是工程师们分析问题和解决问题的两大法宝。所以&#xff0c;在某些测试场景中&#xff0c;如果有时域信息的护法&#xff0c;咱们就能从时频域两个维…

创业目标市场的选择和分析

一、市场细分与选择 创业过程中&#xff0c;选择目标市场至关重要。市场细分是将大市场分割成更小的、具有相似需求的群体。市场细分的方法主要有地理、人口、心理和行为四种&#xff1a; 地理细分&#xff1a;根据地区、城市、气候等地理因素进行市场划分。例如&#xff0c;…

【普通切换】【DC-based handover】【DAPS】协议栈分析

移动网络切换 移动通信中切换是保证终端业务的基本流程&#xff0c;而切换时延是终端(UE)不能与任何基站交互(传递)用户面数据包的最短时间。 在5G(NR)网络中当终端(UE)接收到切换命令时&#xff0c;将断开与源小区的连接向目标小区发起随机接入过程。在此期间终端(UE)的数据传…

牛客ONT45 距离是K的二叉树节点【中等 宽度优先遍历 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/e280b9b5aabd42c9b36831e522485622 思路 图&#xff0c;队列 构件图&#xff0c;直接从target出发&#xff0c;扩展到第k层就是答案Java代码 import java.util.*;/** public class TreeNode {* int val 0;* …

架构设计之安全性属性深度剖析:从理论到实践的完美融合

文章目录 引言一、安全性属性的理论探讨1.1 定义说明1.2 安全原则1.3 安全模型1.4 安全机制 二、安全性属性的实践应用2.1 安全风险评估2.2 架构设计中的安全考虑2.3 技术手段和工具2.4 团队协作与沟通2.5 安全政策和流程2.6 合规性和标准2.7 持续监控和改进 三、理论与实践的融…

Python函数进阶

文章目录 1 函数多返回值2 函数多种传参方式2.1 位置参数2.2 关键字参数2.3 缺省参数2.4 不定长参数 3 匿名函数函数作为参数传递lambda匿名函数 1 函数多返回值 def test_return():return 1,2,3 x,y,z test_return() print(x) print(y) print(z)2 函数多种传参方式 2.1 位置参…