【算法速成课2 | 题单】背包问题

专栏指路:《算法速成课》


前导:

动态规划问题中最入门、也最多变的,当属背包问题

简单来说,就是在有限的空间,(花费最小的代价)达成最大的收益

本文会讲一些常见的背包问题(可通过目录挑选题目),难度普及提高左右,配套一些例题。

个人认为比 oiwiki 更适合初学者一点,不过肯定没人家全。

(1)0/1 背包

有 n 个物品,每物品的重量为 a_i

我们有一个最大承重为 v 的背包。

从 n 个物品中选若干个装入背包,使得所选物品的和 s 尽量接近 v (s\leq v) ,即 v - s 的值最小。

标题的 0 和 1 指的是每个物品有不选和选两种状态。

根据这个定义状态 f[i] 为 1 就表示背包里放 i 重量是可能的,0 则不可能。

代码:

#include<bits/stdc++.h>
using namespace std;int a[30];
bool f[20010];   //f[i]:1 表示背包里放 i重量是可能的,0 则不可能 
//要多开一点空间,以防爆炸 int main() {ios::sync_with_stdio(false);   //关同步流,可以让 cin更快一点 cin.tie(0);   //打了这两行就不可以用 scanf和 printf了 int V, n;cin >> V >> n;for (int i = 1; i <= n; i++) {cin >> a[i];    //读入 }memset(f, 0, sizeof(f));   //初始化 f[0] = 1;           //一开始背包里啥也没有,0 是可能的 for (int i = 1; i <= n; i++) {for (int j = V; j >= a[i]; j--) {   //因为每个东西只有一个,所以要反着放//正着放就可以一种东西叠加,适合一个东西有无限个的情况 if (f[j - a[i]] == 1) {f[j] = 1;   //如果 j - a[i]是可能的,那么再放一个 a[i]也是可能的 }}}int p;for (int i = V; i >= 0; i--) {  //找最大的那个可能的 if (f[i] == 1) {p = i;break;}}cout << V - p << "\n";   //输出差值 return 0;
}

例题:

洛谷 P2925 [USACO08DEC] Hay For Sale S

例题可以和上面代码一样做,但还可以有一种写法:

#include <bits/stdc++.h>
using namespace std;int f[50010], a[5010];
//f[i]:表示给你 i空间,你最多可以放多少
//f[i] <= i int main() {ios::sync_with_stdio(false);cin.tie(0);int V, n; cin >> V >> n;for (int i = 1; i <= n; i++) {cin >> a[i];    //读入 }memset(f, 0, sizeof(f));for (int i = 1; i <= n; i++) {for (int j = V; j >= a[i]; j--) {f[j] = max(f[j], f[j - a[i]] + a[i]);}}cout << f[V];//输出给你 V空间,最多可以放多少return 0;
}

练习:

有 T 个数列。 设 S_i 为从第 i 个数列选若干个数的和。

(当然,一个数列有很多个 S_i

设整数 K 满足所有数列 S_i 中都有 K,求 K 的最大值。

解析:

每个数列都搞一个 0/1 背包,如果这个数列可以达到 i 那么 sum[i]++。

最后找最大的 i 满足 sum[i] = T。

代码就不给了。

例题 2:

小明背着一个背包(最大能带的重量为 T)走进一个山洞。

山洞里有 n 个宝石,第 i 个 宝石的重量为 t_i,拿到宝石店能卖 m_i 块钱。

求最多能卖多少钱。

基本和之前的代码一样,只不过定义状态 f[i] 为给你 i 空间,你最多可以卖多少钱

核心代码:

for (int i = 1; i <= n; i++) {for (int j = T; j >= t[i]; j--) {f[j] = max(f[j], f[j - t[i]] + m[i]);}
}cout << f[T] << "\n";

例题 3(有点难):

洛谷P3010 [USACO11JAN] Dividing the Gold S

其实很简单,想不通的话看代码一下就懂了。

就是模数模完可能是零有点恶心:

#include<bits/stdc++.h>
using namespace std;const int N = 310, M = 6e5 + 10;   //最多就是 n * a[i]
const int P = 1000000;int a[N], f[M];  //f[i]: 有多少种加数方案能得到 i
bool g[M]; //g[i]: 0不可能,1可能
//因为对 1,000,000 取余完可能为 0,所以要多开一个 g 数组判断当前 i 是否能得到int main() {ios::sync_with_stdio(false);cin.tie(0);int n;cin >> n;int sum = 0;for (int i = 1; i <= n; i++) {cin >> a[i];sum += a[i];}memset(f, 0, sizeof(f));memset(g, 0, sizeof(g));f[0] = 1;g[0] = 1;for (int i = 1; i <= n; i++) {for (int j = sum; j >= a[i]; j--) {f[j] = ( f[j] + f[j - a[i]] ) % P;g[j] |= g[j - a[i]] ;//位运算,如果 g[j - a[i]]等于 1 那么g[j] 也等于 1//反之 g[j] 不变}}for (int i = sum / 2; i >= 0; i--) {if (g[i] != 0) {  // i 可以达到cout << (sum - i) - i << "\n";   // sum - i 是另一组数长度 cout << f[i] << "\n";break;}}return 0;
}

(2)完全背包

有 n 种物品,每物品的重量为 a_i有无限个

我们有一个最大承重为 v 的背包。

从 n 种物品中选若干个装入背包,使得所选物品的和 s 尽量接近 v (s\leq v) ,即 v - s 的值最小。

输入第 i 种宝石的重量为 t_i,拿到宝石店能卖 m_i 块钱。

和上面 0/1 背包的例题 2 一模一样,除了物品有无穷多个。

那么第二个循环就变成正序。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;int t[N], m[N];
int f[N];int main() {ios::sync_with_stdio(false);cin.tie(0);int T, n;cin >> T >> n;for (int i=1; i<=n; i++) {cin >> t[i] >> m[i];}memset(f, 0, sizeof(f)); for (int i = 1; i <= n; i++) {for (int j = t[i]; j <= T; j++) {    //就是这里!!改成正序的,就代表单种物品可以自己和自己叠加f[j] = max(f[j], f[j - t[i]] + m[i]);}}cout << f[T] << "\n";return 0;
}

稍有难度的例题:

洛谷 P3027 [USACO10OCT] Making Money G

洛谷那边的题面就像一坨烤糊的司康,,在那边提交就行了题意还是看我写的:

小明带着 m 块钱走进一个山洞挖宝石,然后带着宝石到市场去卖。

山洞里有 n 种宝石(每种宝石无限多个),第 i 种宝石的价格为 c_i,拿到宝石店能卖 r_i 块钱。

假如小明能在市场卖掉所有采购的宝石,

求小明所获得的最大利润(未花完的钱算入利润里面)的最大值。

解析:

换汤不换药,一开始就把 r 数组转换成利润,之后正常完全背包。

最后从 f[m] 找到第一个 f 值不等于 f[m] 的值的前一位 p,就是真正买宝石花的钱。

输出答案时 f[m] + m - p,因为 m - p 是未花完的钱。

#include<bits/stdc++.h>
using namespace std;const int N = 110, M = 1e5 + 10;int c[N], r[N];
int f[M];   // f 要开大点int main() {ios::sync_with_stdio(false);cin.tie(0);int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) {cin >> c[i] >> r[i];r[i] -= c[i];   //先把 r 数组转化成利润}memset(f, 0, sizeof(f)); for (int i = 1; i <= n; i++) if(r[i] > 0) {   //有利润才干事for (int j = c[i]; j <= m; j++) {f[j] = max(f[j], f[j - c[i]] + r[i]);}}int p = m;while ( p > 0 && f[p] == f[p - 1]) {    //寻找真正发生“质变”的那个数,那才是真正花的钱p --;}cout << f[p] + m - p << "\n";return 0;
}

有点小巧思的例题:

P2563 [AHOI2001] 质数和分解 - 洛谷

数据范围很小,很多种方法都可以过。

这里我就用线性筛筛出质数,然后在质数数组上完全背包。

代码:

#include<bits/stdc++.h>
using namespace std;const int N = 310;
bool v[N];
int pr, prime[N], f[N];void init() {memset(v, 0, sizeof(v));v[1] = 1;pr = 0;for (int i = 2; i <= N - 10; i++) {if (!v[i]) {pr ++;prime[pr] = i;}for (int j = 1; j <= pr && i * prime[j] <= N - 10; j++) {int pj = prime[j];v[i * pj] = 1;if (i % pj == 0) {break;}}}
}int main() {init();int n;while (scanf("%d", &n) != EOF) {memset(f, 0, sizeof(f));f[0] = 1;for (int i = 1; i <= pr; i ++) {for (int j = prime[i]; j <= n; j ++) {f[j] += f[j - prime[i]];}}cout << f[n] << "\n";}return 0;
}

(3)多重背包

和 0/1 背包一样,只不过这次每种物品有固定的个数。

例题 & 二进制优化:

281. 硬币 - AcWing题库

解析:

为了方便学校 oj 使用,我单开了一篇。

麻烦大家点这个链接。

例题 & 单调队列优化:

(自行百度/oiwiki 单调队列是什么)

P1776 宝物筛选 - 洛谷

解析:

为了方便学校 oj 使用,我又单开了一篇。

麻烦大家点这个。

背包问题的基础就到这里啦,还有些变种问题,以后可能会讲。

感谢您的阅读。

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

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

相关文章

计算机视觉与深度学习 | 深度学习图像匹配算法在不同纹理复杂度场景下的鲁棒性和计算效率评估方法

如何评估深度学习图像匹配算法在不同纹理复杂度场景下的鲁棒性和计算效率? 文章目录 如何评估深度学习图像匹配算法在不同纹理复杂度场景下的鲁棒性和计算效率? 一、评估框架概述 1.1 核心评估维度 1.2 评估流程 二、纹理复杂度场景分类方法 2.1 纹理特征量化指标 2.2 场景分…

AI 提示词工程与上下文工程:从入门到深入的系统实践指南

前言近年来&#xff0c;随着大语言模型&#xff08;LLM&#xff0c;Large Language Model&#xff09;的快速发展&#xff0c;提示词工程&#xff08;Prompt Engineering&#xff09;与上下文工程&#xff08;Context Engineering&#xff09;逐渐成为 AI 应用开发中至关重要的…

救火!Linux服务器慢如蜗牛:一套从根源到应用的性能问题诊断全攻略

前言&#xff1a;从“玄学”到“科学” “服务又卡了&#xff01;” 这是我们每个Linux运维/SRE工程师最不想听到&#xff0c;却又最常听到的一句话。随之而来的&#xff0c;往往是开发、产品、甚至老板的连环追问。此时&#xff0c;一个经验不足的工程师可能会立刻登录服务器&…

BYOFF (Bring Your Own Formatting Function)解析(80)

BYOFF (Bring Your Own Formatting Function)解析(80) 看起来不错!要注意的是,我们并没有真正使用任何自定义的特殊标记。其中 “Question”(问题)、“Answer”(答案)、井号(#)以及 EOS 标记,都是分词器词汇表中常见的条目。在本节后续内容中,我们将探讨自定义特…

秋招|MCU+RTOS技术栈——面试八股文整理3:STM32

目录 1.单片机启动流程 2.看门狗 3.最小系统 4.ROM、RAM、Flash 5.EPROM、EEPROM 6.Bootloader与OTA 1.单片机启动流程 单片机的启动流程是指从上电或复位开始到应用用户主程序执行的一系列自动操作过程&#xff0c;不同架构的单片机流程略有差异&#xff0c;但核心逻辑…

在 CentOS 9 上安装 Docker 的完整指南

1.准备安装环境&#xff08;1&#xff09;禁用防火墙与SELinux[rootlocalhost ~]# systemctl disable --now firewalld.service Removed "/etc/systemd/system/multi-user.target.wants/firewalld.service". Removed "/etc/systemd/system/dbus-org.fedoraproj…

如何实现外语播客的中文同传?

Bayt播客可以将任何语言的外语播客&#xff08;英文播客、日文播客、韩文播客等&#xff09;转换成中文音频收听&#xff0c;实现同声传译。并且还提供中文和原文的双语字幕。帮助你跨越语言障碍&#xff0c;收听高质量外语内容 核心功能&#xff1a; 1、所有语言的播客均可转…

Spring Cloud ------ Gateway

一、什么是网关 经常面试的人肯定知道&#xff0c;在去公司面试时&#xff0c;通常不会直接去面试官那里面试&#xff0c;而是先去前台进行询问面试官的所在地&#xff0c;并进行一些相关登记。而网关对于一个微服务项目来说&#xff0c;就类似于一个前台&#xff0c;打到微服…

Go初级之九:Select 与并发控制

在Go语言中&#xff0c;select语句是处理并发编程的核心工具之一。它让我们能够优雅地管理多个通道操作&#xff0c;实现高效的并发控制。 1. Select 语句基础 1.1 Select 的基本语法 package mainimport ("fmt""time" )func main() {ch1 : make(chan stri…

使用 Acme.sh 获取和管理免费 SSL 证书

Acme.sh 是一个开源的 Shell 脚本工具&#xff0c;支持从 Let’s Encrypt 等证书颁发机构获取免费的 SSL/TLS 证书。它支持多种验证方式&#xff0c;并能自动续期证书&#xff0c;适合个人网站或企业使用。 目标 同时支持&#xff0c;主域名和泛域名 安装 Acme.sh获取源码 git …

docker-compose跨节点部署Elasticsearch 9.X集群

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录 前言 一、环境准备 二、遇到的问题与分析 三、配…

【面试场景题】spring应用启动时出现内存溢出怎么排查

文章目录一、定位 OOM 类型二、基础排查&#xff1a;调整 JVM 参数与日志三、堆内存溢出&#xff08;Heap Space&#xff09;排查1. 分析堆转储文件2. 典型场景与解决四、元空间溢出&#xff08;Metaspace&#xff09;排查1. 分析类加载情况2. 典型场景与解决五、直接内存溢出&…

2025年经济学专业女生必考证书指南:打造差异化竞争力

在数字经济快速发展的2025年&#xff0c;经济学专业女生面临着诸多机遇与挑战。单纯的理论知识已经难以满足职场需求&#xff0c;企业更看重解决实际问题的能力&#xff0c;特别是将数据转化为商业洞察的专业技能。各类专业资质认证可以成为系统提升能力的途径之一&#xff0c;…

【CAN通信】AUTOSAR架构下TC3xx芯片是如何将一帧CAN报文接收上来的

目录 前言 正文 1.背景介绍 2.CAN报文硬件原理 3.CAN接收软件实现 3.1. vCan_30_Mcan_Interrupt 3.2. vCan_30_Mcan_RxInterrupt 3.3. vCan_30_Mcan_RxBasicCanHandling 4.总结 前言 在《【CAN通信】AUTOSAR架构下TC3xx芯片是如何将一帧CAN报文发送出去的》一文中我们…

STM32H750 RTC介绍及应用

第十一章 RTC介绍及应用 1. RTC 简介 RTC&#xff08;Real-Time Clock&#xff0c;实时时钟&#xff09;是 STM32H750VBT6 中用于提供日历和时钟功能的低功耗外设&#xff0c;即使主电源关闭&#xff0c;只要 VBAT&#xff08;备份电源&#xff09;供电&#xff0c;RTC 仍能持续…

飞网自适应通信:IPv4 与 IPv6 环境下的高效互联

一、网络连接的难题与飞网的解决方案 在日常生活中&#xff0c;我们常常会碰到这样的场景&#xff1a;在家用手机访问公司电脑里的重要文件&#xff0c;或者远程连接家里的NAS设备查看照片和视频。这些操作都需要设备之间建立起安全又稳定的连接。然而&#xff0c;现实中的网络…

拉格朗日多项式

最近打的几个比赛没意思&#xff0c;不是不会就是不会。不过比赛完后看到别人的WP&#xff0c;感觉受益匪浅。先看一个多项式&#xff1a;当输入Xi时会得到一个Si,要求输入一定数量的xi 来求[c] 当可以输入的x个数与c的个数相同时&#xff0c;可以用矩阵直接求解。&#xff08;…

Vue3 + TypeScript 实现文件拖拽上传

应用效果&#xff1a;实例代码&#xff1a;CommonApplyBasicInfoForm.vue<script setup lang"ts" name"CommonApplyBasicInfoForm"> ...... // 选择文件列表 const selectedFiles ref<FileList | null>(null); // 通过 FormData 对象实现文件…

2025全国大学生数学建模C题保姆级思路模型(持续更新):NIPT 的时点选择与胎儿的异常判定

2025全国大学生数学建模C题保姆级思路模型&#xff08;持续更新&#xff09;&#xff1a;NIPT 的时点选择与胎儿的异常判定&#xff0c;完整持续更新内容见文末名片 胎儿遗传信息检测与临床决策数学建模分析讲义 问题一&#xff1a;Y染色体浓度的影响因素探索——线性回归的“侦…

【笔记】Software Engineering at Google

聚焦核心原则&#xff0c;挑取最让我眼前一亮的实践点&#xff0c;特别是那些能直接启发或解决我当前工作中痛点的部分。0. 序言 最近集中精力速读了关于 ​Google 软件工程实践​ 的诸多资料&#xff08;包括官方出版物、工程博客、技术演讲以及社区讨论&#xff09;。面对 G…