T2,3,4,5,9动态背包问题

news/2024/9/30 1:40:14

前言

本文主要介绍常见的四种背包问题,思维导图如下:

一、01背包

💡 现有 N 件物品和一个最多能承重 M 的背包,第 i 件物品的重量是 wi​,价值是 vi​。在背包能承受的范围内,试问将哪些物品装入背包后可使总价值最大,求这个最大价值。

因为每件物品只有选与不选两种状态,所以该问题又称01背包问题。

image-20240412130706125

题目链接:AcWing 2. 01背包问题

#include <bits/stdc++.h>using namespace std;const int N = 1010;int w[N], v[N];
int dp[N][N];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {if (j < w[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);}}cout << dp[n][m] << "n";return 0;
}

时间复杂度为O(nm)。

1.1 使用滚动数组优化

image-20240412130806763

image-20240412130834076

⚠️ 请牢记该式,后续讲解完全背包时会提到它。

这显然是错误的。事实上,让 j 从大到小遍历就不会出现这个问题。

#include <bits/stdc++.h>using namespace std;const int N = 1010;int w[N], v[N];
int dp[N];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];for (int i = 1; i <= n; i++)for (int j = m; j >= w[i]; j--)dp[j] = max(dp[j], dp[j - w[i]] + v[i]);cout << dp[m] << "n";return 0;
}

当然,w 数组和 v 数组也是不必要的,我们可以边输入边处理,因此可以得到01背包问题的最终版代码:

#include <bits/stdc++.h>using namespace std;const int N = 1010;int dp[N];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;//n是物体数量,m是背包容积for (int i = 1; i <= n; i++) {int w, v;cin >> w >> v;//w: weight  v: valuefor (int j = m; j >= w; j--)dp[j] = max(dp[j], dp[j - w] + v);}cout << dp[m] ;return 0;
}

到此为止,可以总结出,当dp 数组是二维数组时, j 既可以从小到大遍历也可以从大到小遍历,但当 dp 数组是一维数组时, j只能从大到小遍历

二、完全背包

💡 现有 N 种物品和一个最多能承重 M 的背包,每种物品都有无限个,第 i 种物品的重量是 wi​,价值是 vi​。在背包能承受的范围内,试问将哪些物品装入背包后可使总价值最大,求这个最大价值。

image-20240412131025001

题目链接:AcWing 3. 完全背包问题

#include <bits/stdc++.h>using namespace std;const int N = 1010;int w[N], v[N];
int dp[N][N];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) {int t = j / w[i];for (int k = 0; k <= t; k++)dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w[i]] + k * v[i]);}cout << dp[n][m] << "n";return 0;
}

image-20240412131048173

2.1 使用滚动数组优化

image-20240412131207539

image-20240412131137792

根据1.1节中的结论,该式对应的是 j 从小到大遍历,于是我们只需把01背包问题的代码中的 j 改为从小到大遍历即可

#include <bits/stdc++.h>using namespace std;const int N = 1010;int dp[N];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) {int w, v;cin >> w >> v;for (int j = w; j <= m; j++)  // 只需修改这一行dp[j] = max(dp[j], dp[j - w] + v);}cout << dp[m] << "n";return 0;
}

优化后的时间复杂度为 O ( n m ) O(nm) O(nm)。

三、多重背包

💡 现有 N 种物品和一个最多能承重 M 的背包,第 i 种物品的数量是 si​,重量是 wi​,价值是 vi​。在背包能承受的范围内,试问将哪些物品装入背包后可使总价值最大,求这个最大价值。

image-20240412131255569

题目链接:AcWing 4. 多重背包问题

#include <bits/stdc++.h>using namespace std;const int N = 110;int dp[N][N];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) {int w, v, s;cin >> w >> v >> s;for (int j = 1; j <= m; j++) {int t = min(s, j / w);  // 只有这里需要修改for (int k = 0; k <= t; k++)dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w] + k * v);}}cout << dp[n][m] << "n";return 0;
}

image-20240412131316312

3.1 使用二进制优化

image-20240412131338482

题目链接:AcWing 5. 多重背包问题 II

image-20240412131349381

#include <bits/stdc++.h>using namespace std;const int N = 11010, M = 2010;int w[N], v[N];
int dp[M];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;int cnt = 0;while (n--) {int a, b, s;  // a是重量, b是价值, s是数量cin >> a >> b >> s;for (int k = 1; k <= s; k *= 2) {cnt++;w[cnt] = a * k, v[cnt] = b * k;s -= k;}if (s > 0) {cnt++;w[cnt] = a * s, v[cnt] = b * s;}}n = cnt;for (int i = 1; i <= n; i++)for (int j = m; j >= w[i]; j--)dp[j] = max(dp[j], dp[j - w[i]] + v[i]);cout << dp[m] << "n";return 0;
}

image-20240412131400957

四、分组背包

💡 现有 N 组物品和一个最多能承重 M 的背包,每组物品有若干个,同一组内的物品最多只能选一个。每件物品的重量是 wij,价值是vij,其中 i 是组号,j 是组内编号。在背包能承受的范围内,试问将哪些物品装入背包后可使总价值最大,求这个最大价值。

image-20240412131511242

题目链接:AcWing 9. 分组背包问题

#include <bits/stdc++.h>using namespace std;const int N = 110;int w[N][N], v[N][N], s[N];
int dp[N];int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, m;cin >> n >> m;for (int i = 1; i <= n; i++) {cin >> s[i];for (int j = 1; j <= s[i]; j++)cin >> w[i][j] >> v[i][j];}for (int i = 1; i <= n; i++)for (int j = m; j >= 1; j--)for (int k = 1; k <= s[i]; k++)if (j >= w[i][k])dp[j] = max(dp[j], dp[j - w[i][k]] + v[i][k]);cout << dp[m] << "n";return 0;
}

总结

我们可以用一个公式来表示01背包、完全背包和多重背包:

image-20240412131429173

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/29941.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

luogu P4342[IOI1998]Polygon

题目大意 给定一个多边形,对应节点上标记有一个数字,每条边上标记有加(t)或乘(x)表示相邻两个节点可进行的操作,操作后两个节点将合并为一个节点,首先删去一条边(不进行操作),之后在若干次操作后使得该多边形只剩一个节点,且要求所剩节点标记的数最大化,询问最大的…

ES底层原理

1、倒排索引 Elasticsearch 使用一种称为倒排索引的结构,它适用于快速的全文搜索。 有倒排索引,肯定会对应有正向索引:正向索引(forward index) 反向索引(inverted index,实际就是倒排索引)所谓的正向索引,就是搜索引擎会将待搜索的文件都对应一个文件ID,搜索时将这个…

金蝶Kis云代码实现

1、金蝶认证(获取到token)也就是code,获取业务接口网关和auth_data,请求头 RequestHeader类 package com.api.xic.jd.tool; import net.sf.json.JSONObject; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import…

qt实现窗口B始终显示在窗口A上,且上层窗口在电脑任务栏不显示缩图

场景:窗口A上面始终显示窗口B(透明背景) /*****************************************/ 首先,在主窗口即底部窗口重写changeEvent QtGuiApplication1::QtGuiApplication1(QWidget *parent) : QWidget(parent) , m_pQtGuiClass(nullptr) {ui.setupUi(this);setWindowFlags(Q…

微信开发者工具请求接口 Provisional headers are shown

前情最近全权负责公司小程序项目的开发,使用的uniapp技术栈。 坑在和服务端联调的时候发现,接口pending很久,而且时不时的报Provisional headers are shown,而且出现的概率很大方法尝试?有可能是浏览器插件拦截了,把有可能推荐的插件全停了也没有用 有可能是防火墙拦截了…

时序图

时序图时序图1. 参考资料 2. 基础 3. 符号3.1. 斜线形式的上升沿、下降沿 3.2. Either or 信号 3.3. 波形省略3.2.1. 虚线 3.2.2. 波浪号3.4. 地址&数据表示4. 实例-WT588F语音芯片时序图4.1. 了解背景 4.2. 分析 4.3. 列逻辑 4.4. 根据逻辑写代码(伪代码)5. 总结 others…

移动端定位打卡

签到按钮脚本 Mobile_NS.getCurrPosition(function(result){var lngdangq = result["lng"];var lathoum = result["lat"];var minDistance = null;//alert("addr"+addr);var dkzt = $f("dkzt").val();//alert(dkzt);if(dkzt==0){//$f(…