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

各种背包问题简述

什么是背包问题?

背包问题(Knapsack Problem)是计算机科学和运筹学中的一个经典组合优化问题。给定一组物品,每个物品都有其特定的体积(或重量)和价值,在不超过背包总容量的情况下,如何选择物品装入背包,使得背包中物品的总价值最大化。(在学习背包问题之前,笔者建议先阅读笔者写于之前的动态规划入门:从记忆化搜索到动态规划以便更容易理解背包问题)

背包问题的多种变体:

01背包问题:每种物品只能选或不选(0次或者1次)
完全背包问题:每种物品可以选择无数次
多重背包问题:每种物品有数量限制
分组背包问题:物品被分成若干组,每组只能选一个物品
混合背包:以上四种背包问题混在一起
多维费用的背包问题:限制条件不只有体积,还会有其他因素(比如重量)
下面我将借助一些例题带读者深入理解各种背包问题:

01背包:

题目描述:有一个容量为 V 的背包和 N 件物品,每件物品有一个体积 v[i] 和一个价值 w[i]。要求从这 N 件物品中选择若干件放入背包,使得背包中物品的总体积不超过 V,且总价值最大。每件物品只能选择一次(即不能重复选择)。

看到这个问题,相信很多同学第一反应是利用贪心策略,但贪心有三种不同的情况,分别是:

  1. 尽可能选价值较大的物品
  2. 尽可能选提交较小的物品
  3. 尽可能选 w/v(价值 / 体积)较大的物品

但这三种策略都是可以证明为否的,且很容易举出反例,因此贪心策略不可行。
这时我们就可以考虑利用动态规划来解决这个问题,背包问题究其本质也是动态规划的一种,因此,我们可以用动态规划同款的方式来解决问题:
在体积不超过 j 的情况下,从[1, i]区间挑选物品,我们可以选择是否选择第 i 号物品,如果不选择,那么它的结果与从[1, i - 1]区间挑选物品结果一致,即f[i][j] = f[i - 1][j],如果选择第 i 个元素,那么之前位置所选的物品体积总合不能超过 j - v[i],即,f[i][j] = w[i] + f[i-1][j - v[i]]
算法原理

  1. 状态表示:f[i][j]表示:在 [1,i] 区间挑选物品,总体积不能超过 j,所有选法下,最大的价值
  2. 状态转移方程:f[i][j] = max(f[i - 1][j], f[i-1][j - v[i]] + w[i])(如果j-v[i] >= 0)
  3. 初始化:将第一行全部初始化为0
  4. 填表顺序:从上往下每一行
  5. 最终结果:f[n][v]

代码实现:

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 - 1][j - v[i]] + w[i]);}}}cout << f[n][m] << endl;return 0;
}

空间优化:可以将 f 数组从二维降为一维。由于每次更新时都是顺序处理,且使用过的旧值不会被重复利用,因此可以不断覆盖原数组,仅保留一维数组来记录最终结果。可以将 f 数组从二维降为一维。由于每次更新时都是从右往左顺序处理,且使用过的旧值不会被重复利用,因此可以不断覆盖原数组,仅保留一维数组来记录最终结果。

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 = m; j >= v[i]; j--)//修改遍历顺序{f[j] = max(f[j], f[j - v[i]] + w[i]);}}cout << f[m] << endl;return 0;
}

感觉看明白了的可以移步洛谷P1048采药自行检测。

完全背包:

题目描述:给定一个容量为 V 的背包和 N 种物品,每种物品有无限个可用。第 i 种物品的体积为 v[i],价值为 w[i]。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出这个最大总价值。
同样的,贪心策略同样易证为伪,这里便不再赘述,此时,其他量皆与01背包相同,所以这里主要推导状态转移方程。
在总体积不超过 j 的情况下,从[1, i]区间挑选物品,我们可以选择是否选择第 i 号物品,以及选择几个 i 号物品,当不选择第 i 号物品时 f[i][j] = f[i - 1][j],与前 i - 1 号保持一致。当我们选择一个时 f[i][j] = f[i-1][j-v[i]] + w[i],选择两个时 f[i][j] = f[i-1][j-2 * v[i]] + 2 * w[i],…,依次类推,选择 k 个时 f[i][j] = f[i-1][j-k * v[i]] + k * w[i]。所以,f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + w[i],......,f[i-1][j-k * v[i]] + k * w[i])。但是这样,我们依然不能很直观的看出状态转移方程是什么样子的,那我们在往下看一层,即 f[i][j-v[i]] = max(f[i-1][j-v[i]], f[i-1][j-2 * v[i]] + w[i],......,f[i-1][j-k * v[i]] + (k-1) * w[i])。这时我们可以发现,f[i][j-v[i]]中每一个元素加上 w[i] 就可以和 f[i][j] 中的元素对应上,因此,f[i][j] 中除了 f[i-1][j] 意外所有元素取max即为 f[i][j-v[i]] + w[i]。所以,我们可以明确推导出f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i])。(这种技巧不一定所有的方式都适用,但当看见元素样子差不多时可以尝试这样的方式)
算法原理

  1. 状态表示:f[i][j]表示:从[1,i]区间中挑选,总体积不超过j的情况下,所有选法下,最大价值
  2. 状态转移方程:f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i])
  3. 初始化:f[0][i] = 0
  4. 填表顺序:从上往下,每一行从左往右
  5. 最终结果:f[n][v]

代码实现

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 = 1; j <= n; 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] << endl;

空间优化

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 = 1; j <= n; j++){f[j] = max(f[j], f[j - v[i]] + w[i]);}}cout << f[m] << endl;

感觉看明白了的可以移步洛谷P1616疯狂的采药自行检测。

空间优化小结:

在空间优化下
01背包:第二层 for 循环改为从大到小 从右到左
完全背包:第二层for循环依然为从小到大 从左往右

多重背包:

题目描述:有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 x[i] 件可用,每件体积是 v,价值是 w。求解将哪些物品装入背包可使这些物品的总体积不超过背包容量,且总价值最大。
由于多重背包的状态转移方程分析部分与完全背包完全相同,因此在这里便不过多赘述。即f[i][j-v[i]] = max(f[i-1][j-v[i]], f[i-1][j-2 * v[i]] + w[i],......,f[i-1][j-k * v[i]] + (k-1) * w[i])。由于每种物品的可用件数不一致,因此不能像我完全背包一样做优化,直接用一个状态来表示若干内容,需要一次一次挨个比对。
算法原理

  1. 状态表示:f[i][j]表示:从[1,i]区间中挑选,总体积不超过j的情况下,所有选法下,最大价值
  2. 状态转移方程:f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i])
  3. 初始化:f[0][i] = 0
  4. 填表顺序:从上往下,每一行从左往右
  5. 最终结果:f[n][v]

代码实现

int main()
{cin >> n >> m;for (int i = 1; i <= n; i++)cin >> x[i] >> w[i] >> v[i];for (int i = 1; i <= n; i++)for (int j = m; j >= 0; j--)for (int k = 0; k <= x[i] && k * w[i] <= j; k++)f[i][j] = max(f[i][j], f[i - 1][j - k * w[i]] + k * v[i]);cout << f[n][m] << endl;return 0;
}
分组背包

题目描述:有 N 组物品和一个容量为 V 的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是 v,价值是 w。求解将哪些物品装入背包可使这些物品的总体积不超过背包容量,且总价值最大。
算法原理

  1. 状态表示:f[i][j]表示:从前i个小组中挑选,总重量不超过j的情况下,最大价值
  2. 状态转移方程:f[i][j] = max(f[i-1][j], f[i-1][j - v[k]] + w[k]),其中 k 是第 i 组中的物品。
  3. 初始化:全部为0
  4. 填表顺序:从上往下 每一行从左往右
  5. 结果:f[n][v]

代码实现

int main() {int n, V;cin >> n >> V;vector<vector<pair<int, int>>> groups(n + 1); // 每组物品的v和wvector<vector<int>> f(n + 1, vector<int>(V + 1, 0));for (int i = 1; i <= n; i++) {int s;cin >> s; // 第i组的物品数量while (s--) {int v, w;cin >> v >> w;groups[i].push_back({v, w});}}for (int i = 1; i <= n; i++) {for (int j = V; j >= 0; j--) {f[i][j] = f[i - 1][j]; // 不选第i组的任何物品for (auto item : groups[i]) {if (j >= item.first) {f[i][j] = max(f[i][j], f[i - 1][j - item.first] + item.second);}}}}cout << f[n][V] << endl;return 0;
}

文章转载自:

http://totLBs0R.qfmcm.cn
http://PxH8R0eA.qfmcm.cn
http://pva3zYsf.qfmcm.cn
http://Vgj7FUP3.qfmcm.cn
http://2D9ZSYY1.qfmcm.cn
http://TQrz5YPU.qfmcm.cn
http://zTZnz0d6.qfmcm.cn
http://Z5HaFFed.qfmcm.cn
http://wWswxq3d.qfmcm.cn
http://tvqzJYyt.qfmcm.cn
http://lErr9AJt.qfmcm.cn
http://YGB3kRoY.qfmcm.cn
http://d0lcDixs.qfmcm.cn
http://7wwzqXPT.qfmcm.cn
http://Gi0hqf90.qfmcm.cn
http://na5E6rYG.qfmcm.cn
http://FeZMmx0c.qfmcm.cn
http://qEXmVyWY.qfmcm.cn
http://n5iAtab0.qfmcm.cn
http://cvEUZDAR.qfmcm.cn
http://eAuLEyhP.qfmcm.cn
http://v77rJGGj.qfmcm.cn
http://csfKRx3n.qfmcm.cn
http://w5H2dSYY.qfmcm.cn
http://g1ay0C08.qfmcm.cn
http://gI6bcGpK.qfmcm.cn
http://6oWKr54u.qfmcm.cn
http://ndQx1akZ.qfmcm.cn
http://lDYu0XGl.qfmcm.cn
http://IkcCz4if.qfmcm.cn
http://www.dtcms.com/a/369744.html

相关文章:

  • Python反向迭代完全指南:从基础到高性能系统设计
  • CRYPT32!ASN1Dec_SignedDataWithBlobs函数分析之CRYPT32!ASN1Dec_AttributesNC的作用是得到三个证书
  • vcenter管理的4台安装了esxi机器组成的HA,故障后自恢复理解
  • 智慧医疗——解读医院智慧管理分级评估标准体系【附全文阅读】
  • C++ map和set
  • js闭包问题
  • 【教学类-36-10】20240905(通义万相)-A4各种大小的鱼制作“吐泡泡的鱼”01版
  • 【工具变量】上市公司企业海外业务收入数据集(2003-2024年)
  • 从技术选型到现场配置:DDC 楼宇自控系统全流程落地方案(2025 版)
  • 阿里云ecs 2h2g 实际可用内存不足的情况
  • 【React】性能提升方案:Reat.memo, useMemo,useCallback用法详解
  • 文心快码已支持Kimi-K2-0905模型
  • 6.ImGui-颜色(色板)
  • biocmanager安装 库 老是提示网络连接错误 才尝试各种办法
  • 雨后阳光为何更强烈?
  • 数据加盐处理(密码加盐)
  • 本地 Ai 离线视频去水印字幕!支持字幕、动静态水印去除!
  • 文件不展示Eslint的报错红色
  • vggt代码详解
  • 使用海康威视 SDK 实现软触发拍照(C语言完整示例 + 中文注释)
  • 本科论文抽检档案整理:Python批量文件查找、打包、改名
  • 【Day 22】94.二叉树的中序遍历 104.二叉树的最大深度 226.翻转二叉树 101.对称二叉树
  • swing笔记
  • IPD模式下跨部门团队管理
  • Transformer核心—自注意力机制
  • 可搜索且多选的下拉式列表
  • 《C++ printf()函数的深度解析》
  • HTML基础(决定页面结构)
  • Modbus RTU 协议介绍
  • 掌握RabbitMQ核心战法:从消息确认到高可用集群