当前位置: 首页 > news >正文

【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; }
http://www.cnnetsun.cn/news/4343613.html

相关文章:

  • 美团前端移动端笔试复盘:核心考点与手写代码实战思路
  • 飞牛NAS内网穿透实战:零公网IP实现远程访问
  • 东芝REGZA ZX电视:如何通过画质引擎与Mini LED技术实现沉浸式观影
  • 开源象棋引擎核心原理与二次开发实战解析
  • HIS系统毕业设计实战:SSM框架+RABC权限管理全解析
  • 我的世界跨版本联机服务器搭建:Java版与基岩版共存方案详解
  • Fable 5.1与Opus 5.1延期发布:版本管理与升级准备指南
  • 时间序列预测实战:用Prophet和LightGBM预测8月14日业务指标
  • 超薄嵌入式冰箱怎么选?尺寸、底部散热与安装全解析
  • 基于PHP+SQL的成绩查询系统毕业设计:从数据库设计到答辩全攻略
  • 六轴运动控制上位机开发实战:C# WinForm从零到一
  • 卫宁PACS阅片器深度解析:从DICOM协议到三维重建与部署实战
  • Grok Bot与OpenClaw:搜索热度之外的智能体选型与本地部署指南
  • 吴恩达NLP专项课程全解析:从词向量到Transformer的实战笔记
  • 奇安信秋招测试岗笔试解析:从Linux到安全测试思维
  • 栅格地图上的牛耕式分区:全覆盖路径规划的实用实现
  • Mac Studio本地跑Qwen3.8 27B:内存、量化与推理框架实测
  • 用YOLOv8实现双马尾检测:从本地部署到API封装完整指南
  • EasyUI DataGrid分页实战:SSM项目中的参数、SQL与排错全解
  • Grok Bot接入实战:API调用、本地部署与虚拟信用卡代购风险解析
  • 基于DSP28335的三电平SVPWM算法实现与调试
  • 毕业写论文不用乱氪金!一站式学术 AI,帮你省下查重会员钱
  • Replit智能路由与企业功能实战:从云端部署到灰度发布的完整指南
  • LeetCode题库压缩包:从解压避坑到打造个人刷题工作区
  • 开放世界多智能体自主数学发现:框架设计与工程实践
  • MKVToolNix v95.0:无损视频容器处理与自动化脚本实战
  • 3D人脸识别智能门锁深度解析:从防攻击原理到德施曼Q2FD选购验证指南
  • 蚂蚁工程数据挖掘岗笔试全解析:从特征工程到SQL优化
  • 嵌入式状态机与事件驱动架构:从混乱逻辑到可控设计
  • 嵌入式裸机用定时器模拟任务:从超级循环到轻量级时间片调度