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

devtools/2024/12/21 21:50:04/

背包问题

  • 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.ppmy.cn/devtools/45368.html

相关文章

《QT实用小工具·六十七》QTabWidget实现的炫酷标签工具栏

1、概述 源码放在文章末尾 该项目基于QTabWidget和QTabBar实现了灵活的标签工具栏&#xff0c;主要包含如下功能&#xff1a; 1、标签栏可以收起&#xff0c;可以展开 2、可以在标签栏中添加新的标签界面 3、可以从标签工具栏中把界面拖出来&#xff0c;也可以拖回去 4、关闭拖…

ChaosBlade混沌测试实践

ChaosBlade: 一个简单易用且功能强大的混沌实验实施工具 官方仓库&#xff1a;https://github.com/chaosblade-io/chaosblade 1. 项目介绍 ChaosBlade 是阿里巴巴开源的一款遵循混沌工程原理和混沌实验模型的实验注入工具&#xff0c;帮助企业提升分布式系统的容错能力&…

Three.js 中的场景与相机基础

Three.js 中的场景与相机基础 一、场景&#xff08;Scene&#xff09; 在 Three.js 中&#xff0c;场景是所有 3D 对象存在和交互的容器。艾斯视觉作为行业ui设计与前端开发服务商很高兴能在这里与你共同探讨&#xff1a;它就像是一个虚拟的 3D 空间&#xff0c;我们可以在其中…

面试必问:MySQL死锁是什么,如何解决?(史上最全)

MySQL死锁接触少&#xff0c;但面试又经常被问到怎么办&#xff1f; 最近有小伙伴在面试的时候&#xff0c;被问了MySQL死锁&#xff0c;如何解决&#xff1f; 虽然也回答出来了&#xff0c;但是不够全面体系化&#xff0c; 所以&#xff0c;小北给大家做一下系统化、体系化的…

江苏大信环境科技有限公司:环保领域的开拓者与引领者

2009 年&#xff0c;江苏大信环境科技有限公司在宜兴环保科技工业园成立。自创立之始&#xff0c;该公司便笃定坚守“诚信为本、以质量求生存、以创新谋发展”这一经营理念&#xff0c;全力以赴为客户构建专业的工业有机废气治理整体解决方案&#xff0c;进而成为国家高新技术企…

不靠后端,前端也能搞定接口!

嘿&#xff0c;前端开发达人们&#xff01;有个超酷的消息要告诉你们&#xff1a;MemFire Cloud来袭啦&#xff01;这个神奇的东东让你们不用依赖后端小伙伴们&#xff0c;也能妥妥地搞定 API 接口。是不是觉得有点不可思议&#xff1f;但是事实就是这样&#xff0c;让我们一起…

Chromebook Plus中添加了Gemini?

Chromebook Plus中添加了Gemini&#xff1f; 前言 就在5月29日&#xff0c;谷歌宣布了一项重大更新&#xff0c;将其Gemini人工智能技术集成到Chromebook Plus笔记本电脑中。这项技术此前已应用于谷歌的其他设备。华硕和惠普已经在市场上销售的Chromebook Plus机型&#xff0c;…

Mac上安装harbor

在Mac Book VMware Fusion 虚拟出来的 ubuntu&#xff08;22.04.4&#xff09;的环境中安装官方离线版本 harbor-offline-installer-v2.10.2.tgz会出现如下错误&#xff1a; prepare base dir is set to /home/zhangzk/harbor WARNING: The requested images platform (linux/…