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

多重背包、分组背包、混合背包和多维背包

一、多重背包

1、介绍

物品个数是多个,但不是无穷个。

例题:多重背包

登录—专业IT笔试面试备考平台_牛客网

2、分析

dp[i][j]表示在[1, i]中选,重量不超过j的最大价值
不选i:dp[i][j] = dp[i - 1][j];
选i:由于物品个数不是无限的,所以不能优化只能一个一个写
选一个:dp[i][j] = dp[i - 1][j - w[i]] + v[i];
选二个:dp[i][j] = dp[i - 1][j - 2 * w[i]] + 2 * v[i];
..... 
选最多x[i]个:dp[i][j] = dp[i - 1][j - x[i] * w[i]] + x[i] * v[i];
当然枚举过程中j - k * w[i] >= 0
初始化返回值都和之前问题一样

3、代码

#include <bits/stdc++.h>
using namespace std;
int w[110];
int v[110];
int x[110];
int n, t;int dp[110][110];
int main()
{cin >> n >> t;for(int i = 1; i <= n; i++)cin >> x[i] >> w[i] >> v[i]; for(int i = 1; i <= n; i++){for(int j = 0; j <= t; j++){for(int k = 0; k <= x[i] && j - k * w[i] >= 0; k++)dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w[i]] + k * v[i]);}}cout << dp[n][t];return 0;
}

4、空间优化

只用到了左上方的数据,所以从右向左

#include <bits/stdc++.h>
using namespace std;
int w[110];
int v[110];
int x[110];
int n, t;int dp[110];
int main()
{cin >> n >> t;for(int i = 1; i <= n; i++)cin >> x[i] >> w[i] >> v[i]; for(int i = 1; i <= n; i++){for(int j = t; j >= 0; j--){for(int k = 0; k <= x[i] && j - k * w[i] >= 0; k++)dp[j] = max(dp[j], dp[j - k * w[i]] + k * v[i]);}}cout << dp[t];return 0;
}

5、二进制优化

二进制优化:优化后最终结果会把多重背包变成01背包
其实多重背包不做任何优化也能看作01背包,因为个数有限,可以把每一个物品拆出来
基于这种思想不难想到可以把同一种物品的10进制个数通过二进制表示出来,这样优化的是logX
假设一个物品个数是9,重量w,价值v
9 = 1 + 2 + 4 + 2,即可以看成有4种物品分别的重量和价值是[w, v], [2w, 2v], [4w, 4v], [2w, 2v]
然后进行01背包处理
这样从O(n * t * x)->O(n * t * logx)
没有数量数组,原数组由于优化,每种物品分成了logX种物品,所以大小还要乘logX

求方案数不能二进制优化,因为同一种物品被我们分成了logx种物品
即优化后的不同数据不能代表不同的物品,也就不能代表一种方案

int w[5 * 110];
int v[5 * 110];
int n, t;
int sz; // 新的个数用sz记录
int dp[110];
int main()
{cin >> n >> t;for(int i = 1; i <= n; i++){int x, y, z;cin >> x >> y >> z;// 同一物品开始二进制分类int p = 1;while(x >= p){sz++;x -= p;w[sz] = y * p;v[sz] = z * p;p *= 2;}if(x){sz++;w[sz] = y * x;v[sz] = z * x;}}for(int i = 1; i <= sz; i++){for(int j = t; j >= w[i]; j--){dp[j] = max(dp[j], dp[j - w[i]] + v[i]);}}cout << dp[t];return 0;
}

二、分组背包

1、介绍

在每一组中挑一个物品

例题:分组背包

P1757 通天之分组背包 - 洛谷

2、分析

dp[i][j]表示在小组[1, i]中选,每组选一个物品,重量不超过j的最大价值
不选i: dp[i][j] = dp[i - 1][j];
选i: 选i组的第一个: dp[i][j] = dp[i - 1][j - w[i][0]] + v[i][0];
选i: 选i组的第二个: dp[i][j] = dp[i - 1][j - w[i][1]] + v[i][1];
.....
选i: 选i组的第x个: dp[i][j] = dp[i - 1][j - w[i][x - 1]] + v[i][x - 1];

3、代码

// https://www.luogu.com.cn/problem/P1757
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
int n, m;
vector<pii> g[110];
int k = 0;int dp[110][1010];
int main()
{cin >> m >> n;for(int i = 1; i <= n; i++){int W, V, K;cin >> W >> V >> K;g[K].push_back({W, V});k = max(k, K);}for(int i = 1; i <= k; i++){for(int j = m; j >= 0; j--){dp[i][j] = dp[i - 1][j];for(auto& [a, b] : g[i]){if(j - a >= 0)dp[i][j] = max(dp[i][j], dp[i - 1][j - a] + b);}}}cout << dp[k][m];return 0;
}

4、空间优化

空间优化:用的是左上,所以从右向左

int dp[1010];
int main()
{cin >> m >> n;for(int i = 1; i <= n; i++){int W, V, K;cin >> W >> V >> K;g[K].push_back({W, V});k = max(k, K);}for(int i = 1; i <= k; i++){for(int j = m; j >= 0; j--){for(auto& [a, b] : g[i]){if(j - a >= 0)dp[j] = max(dp[j], dp[j - a] + b);}}}cout << dp[m];return 0;
}

三、混合背包

1、介绍

01背包,完全背包,多重背包,分组背包结合在一起的背包问题

例题:樱花

P1833 樱花 - 洛谷

2、分析

就是遇到一种物品属于什么背包问题就用对应的状态方程解决即可。

3、代码

int m, n;
int t[10010];
int c[10010];
int p[10010];int dp[10010][1010];
int main()
{int h1, m1, h2, m2;char ch;cin >> h1 >> ch >> m1 >> h2 >> ch >> m2 >> n;m = h2 * 60 + m2 - h1 * 60 - m1;for(int i = 1; i <= n; i++)cin >> t[i] >> c[i] >> p[i];for(int i = 1; i <= n; i++){if(p[i] == 0){for(int j = 0; j <= m; j++){dp[i][j] = dp[i - 1][j];if(j - t[i] >= 0)dp[i][j] = max(dp[i][j], dp[i][j - t[i]] + c[i]);}}else {for(int j = m; j >= 0; j--){dp[i][j] = dp[i - 1][j];for(int k = 1; k <= p[i] && j - k * t[i] >= 0; k++)dp[i][j] = max(dp[i][j], dp[i - 1][j - k * t[i]] + k * c[i]);}}}cout << dp[n][m];return 0;
}

4、空间优化

注意完全背包是从左向右即可

int dp[1010];
int main()
{int h1, m1, h2, m2;char ch;cin >> h1 >> ch >> m1 >> h2 >> ch >> m2 >> n;m = h2 * 60 + m2 - h1 * 60 - m1;for(int i = 1; i <= n; i++)cin >> t[i] >> c[i] >> p[i];for(int i = 1; i <= n; i++){if(p[i] == 0){for(int j = t[i]; j <= m; j++){dp[j] = max(dp[j], dp[j - t[i]] + c[i]);}}else {for(int j = m; j >= t[i]; j--){for(int k = 1; k <= p[i] && j - k * t[i] >= 0; k++)dp[j] = max(dp[j], dp[j - k * t[i]] + k * c[i]);}}}cout << dp[m];return 0;
}

四、多维背包

1、介绍

限制条件增多的背包问题,必须要空间优化。

例题:L 国的战斗之间谍

P1910 L 国的战斗之间谍 - 洛谷

2、分析

dp[i][j][k]表示在[1, i]中选,暴露不超过j,工资不超过j的最大价值
不选i: dp[i][j][j] = dp[i - 1][j][k];
  选i: dp[i][j][k] = dp[i - 1][j - b[i]][k - c[i]] + a[i];

3、空间优化代码

从右向左

// https://www.luogu.com.cn/problem/P1910
// 多维背包:限制条件变多,那就加维数
#include <bits/stdc++.h>
using namespace std;
int n, m, x;
int a[110];
int b[110];
int c[110];
int dp[1010][1010];// dp[i][j][k]表示在[1, i]中选,暴露不超过j,工资不超过j的最大价值
// 不选i: dp[i][j][j] = dp[i - 1][j][k];
//   选i: dp[i][j][k] = dp[i - 1][j - b[i]][k - c[i]] + a[i];
// 必须空间优化
int main()
{cin >> n >> m >> x;for(int i = 1; i <= n; i++)cin >> a[i] >> b[i] >> c[i];for(int i = 1; i <= n; i++){for(int j = m; j >= b[i]; j--){for(int k = x; k >= c[i]; k--){dp[j][k] = max(dp[j][k], dp[j - b[i]][k - c[i]] + a[i]);}}}cout << dp[m][x];return 0;
}

五、例题

1、摆花

P1077 [NOIP 2012 普及组] 摆花 - 洛谷

#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[110];
const int mod = 1e6 + 7;// dp[i][j]表示在[1, i]种物品种选,个数刚好j的总方案数
// 不选i:   dp[i][j] = dp[i - 1][j];
// 选一个i: dp[i][j] += dp[i - 1][j - 1];
// 选二个i: dp[i][j] += dp[i - 1][j - 2];
// ....
// 选a[i]个i: dp[i][j] += dp[i - 1][j - a[i]];
// j - x >= 0;
// 初始化:dp[0][0] = 1; dp[0][j] = 0int main()
{cin >> n >> m;vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));dp[0][0] = 1;for(int i = 1; i <= n; i++)cin >> a[i];for(int i = 1; i <= n; i++){for(int j = 0; j <= m; j++){for(int k = 0; k <= a[i] && j - k >= 0; k++)dp[i][j] = (dp[i][j] + dp[i - 1][j - k]) % mod;}}cout << dp[n][m];return 0;
}

2、排兵布阵

P5322 [BJOI2019] 排兵布阵 - 洛谷

#include <bits/stdc++.h>
using namespace std;
int s, n, m;
int g[110][110];
int dp[110][20010];// https://www.luogu.com.cn/problem/P5322
// 对于每一个人的每一个城堡我们都能*2+1找到最少占领这个城堡的分配士兵数
// 即问题转化成了在每一组城堡中我挑出一个数,计算能占领几个这样的城堡
// dp[i][j]表示在[1, i]的城堡中,派出不超过m名士兵后的最大得分
// 存储数组g[110][110],一维城堡编号,二维玩家编号,每一行排好序
// 不派:dp[i][j] = dp[i - 1][j];
// 派g[i][p]的士兵争夺城堡i: dp[i][j] = dp[i - 1][j - g[i][p]] + i * p;
int main()
{cin >> s >> n >> m;for(int i = 1; i <= s; i++){for(int j = 1; j <= n; j++){cin >> g[j][i];g[j][i] = 2 * g[j][i] + 1;}}for(int i = 1; i <= n; i++)sort(g[i] + 1, g[i] + s + 1);for(int i = 1; i <= n; i++){for(int j = 0; j <= m; j++){dp[i][j] = dp[i - 1][j];for(int p = 1; p <= s; p++){if(j - g[i][p] >= 0)dp[i][j] = max(dp[i][j], dp[i - 1][j - g[i][p]] + p * i);}}}cout << dp[n][m];return 0;
}
http://www.xdnf.cn/news/5642.html

相关文章:

  • 交易所开发-如何开发一个交易所
  • 【C语言】宏经典练习题,交换奇偶位
  • 直播:怎样用Agentic AI搭建企业AI应用?5.24日,拆解新一代“智能客服系统”案例
  • GitDiagram - GitHub 仓库可视化工具
  • 神经网络初步学习——感知机
  • EnumUtils:你的枚举“变形金刚“——让枚举操作不再手工作业
  • 第六章 Java基础-方法
  • 基于STM32、HAL库的BMP388 气压传感器 驱动程序设计
  • HTTP方法和状态码(Status Code)
  • 在Linux中安装JDK并且搭建Java环境
  • 数据处理专题(十三)
  • 讲讲git 和svn
  • HTML5 定位详解:相对定位、绝对定位和固定定位
  • 155.最小栈
  • 【科研】Visio使用
  • 数据同步DataX任务在线演示
  • 码蹄集——人民币大写数字、全部整除、隐晦余8
  • 嵌入式学习笔记 - MSB, LSB
  • 24 小时 AI 门店管家:重新定义连锁门店智能化管理范式
  • 从模型加密到授权交付,CodeMeter赋能3D打印商业化全流程
  • Ubuntu源码版comfyui的安装
  • 组合问题(多集合)
  • idea中ctrl+/注释,总是出现在最前行
  • 【LeeCode】1.两数之和
  • VsCode相关设置
  • 嵌入式工程师的职业发展路径
  • 《数字人生成工具技术研究与探索》
  • K8S Ingress、IngressController 快速开始
  • 什么是Vim
  • spring中的@Lazy注解详解