news 2026/9/1 4:29:29

【C++算法】动态规划背包问题 -> 01背包

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++算法】动态规划背包问题 -> 01背包

01背包的核心是:每个背包只可以用一次

P1048 [NOIP 2005 普及组] 采药 - 洛谷

思路讲解:二维朴素dp

f[i][j]是状态表示

i表示我们要遍历的数组,j表示我们遍历的重量

首先我们看题,我们可以得出:

1、所有的用品,只能选一次

2、求最大值

那么我们怎么使用动态规划呢?

  • 当这个物品的容量超出这个体积的时候,我们是不是不能选
  • 当这个物品小于这个体积的时候,这个物品我们是不是可以考虑

总结:这就涉及到我们的选和不选的问题了


怎么去不选?

当我们遍历i的时候,我们是不是可以不选当前这个物品,我们的状态表示方程

// 不选当前物品 f[i][j]=f[i-1][j]

怎么去选?

当我们遍历 i 的时候,而我们的 j 在遍历重量,是不是只有当我们 j >= 物品的重量才可以去选

注意:我们选完它的物品,此时我们是不是要减去它的重量,在加上它的价值

// 选当前的物品 f[i][j] = max(f[i][j], f[i - 1][j - w[i]]) + v[i];

讲完了,开造!

#include <iostream> using namespace std; const int N = 1005; int x, y; int f[N][N]; int w[N], v[N]; int main() { //输入 cin >> x >> y; for (int i = 1;i <= y;i++)cin >> w[i] >> v[i]; for (int i = 1;i <= y;i++) { for (int j = 0;j <= x;j++) { //不选 f[i][j] = f[i-1][j]; //选 if (j >= w[i]) { f[i][j] = max(f[i][j], f[i - 1][j - w[i]]) + v[i]; } } } cout << f[y][x]; return 0; }

优化dp(滚动数组)

大白话讲解:

场景:你在抄作业

  • 二位数组:你有两张纸,一张是昨天的答案(第i-1行),一张是今天的答案(第i行)。你可以随时参考昨天的答案,不会搞混
  • 一位数组:你只有一张纸,既要保存昨天的答案,又要写今天的答案,还得保证写的时候不能把昨天的答案擦掉

我们先看一下二位数组是怎么存的

场景:你有一张表格

容量0 容量1 容量2 容量3 容量4 物品0 0 0 0 0 0 ← 初始行(没物品) 物品1 0 0 100 100 100 ← 处理完第1个物品 物品2 0 0 100 100 200 ← 处理完第2个物品 物品3 0 0 100 150 200 ← 处理完第3个物品

我们可以发现:

  1. 算第3行(物品3)的时候,只用到了第2行数据
  2. 算完第3行后,第一行、第二行就没用了
  3. 每次只需要上一行的数据

那么怎么用一位数组去节省空间呢?

既然只需要上一行,那我干脆只保留一行,不断覆盖更新

一开始: [0, 0, 0, 0, 0] ← 只有一行 处理物品1: [0, 0, 100, 100, 100] ← 覆盖掉原来的 处理物品2: [0, 0, 100, 100, 200] ← 继续覆盖 处理物品3: [0, 0, 100, 150, 200] ← 继续覆盖

那么省了多少空间?

  1. 二维:物品数 X 容量 个格子
  2. 一位:容量个格子
  3. eg:如果1000个物品,容量1000,二位要100万格子,一维只要1000个

注意:覆盖原来空间也就是数组,就叫做滚动数组


那为什么从大到小呢?

因为在同一个空间改数据,如果不小心,会把没用的旧数据提前覆盖掉!

eg:容量4

1个物品重量2 价值100

数组:[0, 0, 0, 0, 0] 从小到大(从左往右改): j=2: 改成 100 → [0, 0, 100, 0, 0] j=3: 用到 j=1,还是 0 → [0, 0, 100, 100, 0] j=4: 用到 j=2,但 j=2 已经被改成 100 了! 结果:100 + 100 = 200 ❌ 同一个物品用了两次! 从大到小(从右往左改): j=4: 用到 j=2,还是 0 → [0, 0, 0, 0, 100] j=3: 用到 j=1,还是 0 → [0, 0, 0, 100, 100] j=2: 用到 j=0,还是 0 → [0, 0, 100, 100, 100] 结果正确!每个物品只用一次 ✅

总结:

  1. 一位数组=只有一行,反复覆盖更新
  2. 从大到小=从右往左改,避免用刚改过的数据,并且保证每个物品只用一次

#include <iostream> using namespace std; const int N = 10005; int x, y; //int f[N][N]; int f[N]; int w[N], v[N]; int main() { //输入 cin >> x >> y; for (int i = 1;i <= y;i++)cin >> w[i] >> v[i]; for (int i = 1;i <= y;i++) { //for (int j = 0;j <= x;j++) //{ // //不选 // f[i][j] = f[i-1][j]; // //选 // if (j >= w[i]) // { // f[i][j] = max(f[i][j], f[i - 1][j - w[i]]) + v[i]; // } //} for (int j = x;j >= w[i];j--)// 从大到小遍历 { f[j] = max(f[j], f[j - w[i]] + v[i]); } } //cout << f[y][x]; cout << f[x]; return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 4:28:28

美团前端移动端笔试复盘:核心考点与手写代码实战思路

2025年秋招的美团前端&移动端第二批笔试&#xff0c;我是在周六上午完成的。整场线上笔试两个小时&#xff0c;平台用的还是常见的牛客网&#xff0c;体感是题量大、覆盖面广、移动端内容占比明显提升。这套卷子给我最直观的感触是&#xff1a;它不再只问“这个API怎么用”…

作者头像 李华
网站建设 2026/9/1 4:28:17

飞牛NAS内网穿透实战:零公网IP实现远程访问

在家庭或小型办公环境中部署 NAS 系统后&#xff0c;一个核心需求是如何从外部网络安全、便捷地访问到内部的服务。无论是远程管理文件、查看监控录像&#xff0c;还是使用自建的博客、影音库&#xff0c;都需要解决“内网穿透”这个难题。传统的方案如配置路由器端口转发&…

作者头像 李华
网站建设 2026/9/1 4:27:16

东芝REGZA ZX电视:如何通过画质引擎与Mini LED技术实现沉浸式观影

1. 先搞清楚“沉浸式观影”到底需要电视解决哪些问题 “沉浸式观影”这个词现在很常见&#xff0c;但落到一台电视上&#xff0c;它到底意味着什么&#xff1f;很多人会直接想到大屏幕、高分辨率&#xff0c;但这只是基础。真正的沉浸感&#xff0c;是让你在看电影、追剧时&…

作者头像 李华
网站建设 2026/9/1 4:26:19

开源象棋引擎核心原理与二次开发实战解析

简介&#xff1a;这是一份公开源代码的象棋引擎项目&#xff0c;面向象棋游戏开发学习者与编程爱好者&#xff0c;可用于理解棋局评估、棋步生成、搜索策略等核心算法的落地实现。包内共37个文件&#xff0c;以16个h头文件和4个cpp源文件为主&#xff0c;另有BAS、FRM、VBP等Vi…

作者头像 李华
网站建设 2026/9/1 4:25:29

HIS系统毕业设计实战:SSM框架+RABC权限管理全解析

简介&#xff1a;这是一份面向高校计算机/软件工程专业学生的智慧医疗HIS系统毕业设计源码包&#xff0c;基于SpringBoot框架实现&#xff0c;涵盖患者信息管理、预约挂号、电子病历、药品库存、医生排班等常见模块&#xff0c;难度适中&#xff0c;适合用于毕业设计、期末大作…

作者头像 李华
网站建设 2026/9/1 4:23:47

我的世界跨版本联机服务器搭建:Java版与基岩版共存方案详解

很多想开《我的世界》服务器的玩家&#xff0c;遇到的第一道坎并不是不会下载服务端&#xff0c;而是搞不清楚 Java 版和基岩版到底能不能一起玩。网上搜到的方案不是要装一堆看不懂的插件&#xff0c;就是告诉你两个版本必须分开开服。实际上&#xff0c;用目前的跨版本联机方…

作者头像 李华