dp背包九讲(待补充,暂时前两讲)
文章目录
- 背包九讲
- 一、01背包
- 二维dp
- 优化为一维
- 二、完全背包
- 二维dp
- 优化为一维
- 三、多重背包
- 数据范围很小时,变成01背包暴力
- 当数据范围较大时,利用二进制优化
- 二进制优化还不够用时,利用单调队列(滑动窗口)优化
- 四、混合背包
- 五、二维费用的背包问题
- 六、分组背包
- 七、背包问题求方案数
- 八、求背包问题的方案数
- 九、有依赖的背包问题
背包九讲
一、01背包
对于每件物品都有只有一件,体积为 v[i], 价值为 w[i],对于每件物品考虑选或者不选
题目链接
二维dp
f[i][j]表示选完前 i 件物品后能取得的最大价值
数据:
4 5
1 2
2 4
3 4
4 5
手动模拟:
f[0][0] = 0; f[0][1] = 0; f[0][2] = 0; f[0][3] = 0; f[0][4] = 0; f[0][5] = 0;f[1][0] = 0; f[1][1] = 2; f[1][2] = 2; f[1][3] = 2; f[1][4] = 2; f[1][5] = 2;
f[1][1] = max(f[0][1], f[0][1-1]+2) = 2;
f[1][2] = max(f[0][2], f[0][1]+2) = 2;f[2][0] = 0; f[2][1] = 2; f[2][2] = 4; f[2][3] = 6; f[2][4] = 6; f[2][5] = 6;
f[2][1] = f[1][1];
f[2][2] = max(f[1][2], f[1][2-2] + 4) = 4;
f[2][3] = max(f[1][3], f[1][3-2] + 4) = 6;
...最终价值的最大值一定使 f[n][m], 因为所有的价值都是从之前转移过来的最大价值
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;
const int maxn = 1010;int f[maxn][maxn];
int n, m;int main() {// freopen("test.in", "r", stdin);cin >> n >> m;int v[maxn], w[maxn];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-1][j], f[i-1][j-v[i]] + w[i]); // 容量够的话选择价值更大的 }}cout << f[n][m] << endl; return 0;
}
优化为一维
首先我们将 f[i][j] 变成 f[j] 表示容量为 j 时的最大价值
for(int i = 1; i <= n; i++) {for(int j = 0; j <= m; j++) {f[j] = f[j]; // 如果容量不够的话就不选 if(j >= v[i]) f[j] = max(f[j], f[j-v[i]] + w[i]); // 容量够的话选择价值更大的 }
}
还是将物品一件一件判断是否取用来确定价值
我们能看到实际仅仅减小了空间复杂度,而时间复杂度仍然是 O(n^2)
手动模拟一下
f[0] = 0;
i = 1: f[0] = 0; f[1] = 2; f[2] = max(f[2], f[2-1] + 2) = 4, 这显然是错的,这里我们将 1 号物品取用了两次
从上面的手动模拟我们发现, 每次从小容量向大容量更新答案的过程有问题,会经过小容量而重复选择同一个物品,我们很快想到,大容量是通过小容量更新的,那么我们先从大容量开始就会避免重复问题,而对于每个物品实现了选择,又不影响下一个物品利用当前结果更新
for(int i = 1; i <= n; i++) {for(int j = m; j >= 0; j--) {if(j >= v[i]) f[j] = max(f[j], f[j-v[i]] + w[i]);}
}
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;
const int maxn = 1010;int f[maxn];
int n, m;int main() {// freopen("test.in", "r", stdin);cin >> n >> m;int v[maxn], w[maxn];for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];// 从一件物品也不选开始,逐渐利用之前的选择情况继续选择 for(int i = 1; i <= n; i++) {for(int j = m; j >= 0; j--) {if(j >= v[i]) f[j] = max(f[j], f[j-v[i]] + w[i]);}}cout << f[m] << endl; return 0;
}
二、完全背包
每种物品都有无数件,考虑每种物品选几件使价值最大
二维dp
int main() {// freopen("test.in", "r", stdin);cin >> n >> m;for(int i = 1; i <= n; i++) {int v, w;cin >> v >> w;for(int j = 0; j <= m; j++) {f[i][j] = f[i-1][j]; // 如果容量不够的话if(j >= v) f[i][j] = max(f[i-1][j], f[i][j-v] + w); // 如果容量够的话,比较不选和按照当前物品行的小容量来更新大容量二者中的最优值}}cout << f[n][m] << endl;return 0;
}
按理说更新时应该是 max(f[i-1][j], f[i-1][j-v]+w, f[i][j-v]+w),实际上由于上一步对于容量不够的情况下的赋值,所以max(f[i-1][j], f[i][j-v] + w)中已经包括了所有情况,该物品与上一物品的比较再加入第一件该种物品是就已经实现了,在大容量的更新过程中延续
优化为一维
对于完全背包,刚好后面大容量利用小容量更新实现了重复选择的功能,直接由小到大更新即可
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>using namespace std;
const int maxn = 1010;
int n, m;
int f[maxn];int main() {// freopen("test.in", "r", stdin);cin >> n >> m;for(int i = 1; i <= n; i++) {int v, w;cin >> v >> w;for(int j = 0; j <= m; j++) {if(j >= v) f[j] = max(f[j], f[j-v] + w);}}cout << f[m] << endl;return 0;
}
三、多重背包
每种物品有又穷件,考虑如何选择
数据范围很小时,变成01背包暴力
题目链接
数据范围很小,可以将s件i种物品拆分,变成S1+S2+S3+…件物品的 01 背包
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;
const int maxn = 110;int f[maxn];int main() {// freopen("test.in", "r", stdin);int n, m;cin >> n >> m;int v, w, s;// 01背包的扩展 for(int i = 1; i <= n; i++) {cin >> v >> w >> s;for(int j = m; j >= v; j--) {for(int k = 0; k <= s; k++){if(j >= k*v) f[j] = max(f[j], f[j-k*v]+k*w);} }}cout << f[m] << endl;return 0;
}
当数据范围较大时,利用二进制优化
此时题目范围变为 1000 * 2000 * 2000
题目链接
思路:
假如此时有 7 件物品
那我们只需要 1, 2, 4 四个数字就能表示出每件物品
这三个数字是 2 的整次数
0
1 = 1;
2 = 2;
3 = 1 + 2;
4 = 4;
5 = 1 + 4;
6 = 2 + 4;
7 = 1 + 2 + 4;
此时问题转化为只有 1, 2 , 4 三个物品的 01 背包问题
假设此时有 10 件物品
我们只需 1, 2, 3, 4 四个数字就能表示每件物品
3 是怎么来的, 3 = 10 - 1 - 2 - 4,(先选好二进制数然后求出剩下需要补充的数字)
0
1 = 1;
2 = 2;
3 = 3;
4 = 4;
5 = 1 + 4;
6 = 2 + 4;
7 = 1 + 2 + 4;
8 = 1 + 4 + 3;
9 = 2 + 4 + 3;
10 = 1 + 2 + 4 + 3;
此时问题转化为只有 1, 2, 3, 4 四件物品的 01 背包问题
复杂度降为 1000 * log(2000)* 2000 = 1000 * 11 * 2000 = 2 e 7
AC 代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>using namespace std;
const int maxn = 2010;int n, m;
int f[maxn];struct Good{int v, w;
};int main() {// freopen("test.in", "r", stdin);cin >> n >> m;vector<Good> goods; // 由于不确定有多少件物品,开个 vector for(int i = 1; i <= n; i++) {int v, w, s;cin >> v >> w >> s;for(int j = 1; j <= s; j *= 2) { s -= j;goods.push_back({j*v, j*w}); // 存编号为二进制数的物品 }if(s > 0) goods.push_back({s*v, s*w}); // 存剩下的物品 }// 问题转化为只有 goods.size() 件物品的 01 背包问题 for(auto good: goods) {for(int j = m; j >= good.v; j--) {f[j] = max(f[j], f[j-good.v] + good.w);}}cout << f[m] << endl;return 0;
}
二进制优化还不够用时,利用单调队列(滑动窗口)优化
暂时我还不懂,等明白了再解释
AC 代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>using namespace std;
const int maxn = 20010;
int n, m;
int f[maxn], g[maxn], q[maxn];int main() {// freopen("test.in", "r", stdin);cin >> n >> m;for(int i = 1; i <= n; i++) {int c, w, s;cin >> c >> w >> s;memcpy(g, f, sizeof(f));for(int j = 0; j < c; j++) {int hh = 0, tt = -1;for(int k = j; k <= m; k += c) {f[k] = g[k];if(hh <= tt && k - s*c > q[hh]) hh++;if(hh <= tt) f[k] = max(f[k], g[q[hh]] + (k - q[hh]) / c * w);while(hh <= tt && g[q[tt]] - (q[tt] - j) / c*w <= g[k] - (k -j) / c*w) tt--;q[++tt] = k;}}}cout << f[m] << endl;return 0;
}
四、混合背包
五、二维费用的背包问题
六、分组背包
七、背包问题求方案数
八、求背包问题的方案数
九、有依赖的背包问题
dp背包九讲(待补充,暂时前两讲)相关推荐
- 【算法】动态规划+“背包九讲”原理超详细讲解+常见dp问题(9种)总结
目录 一.动态规划(DP) 二.背包九讲 (1)完全背包 P1616 疯狂的采药(完全背包) (2)01背包 滚动数组 一维数组 P1048 采药(01背包) 01背包表格图示 (3)多重背包 整数拆 ...
- 算法--背包九讲(详细讲解+代码)
背包九讲 目录 第一讲 01背包问题 第二讲 完全背包问题 第三讲 多重背包问题 第四讲 混合三种背包问题 第五讲 二维费用的背包问题 第六讲 分组的背包问题 第七讲 有依赖的背包问题 ...
- dalao的背包九讲
背包九讲 目录 第一讲 01背包问题 第二讲 完全背包问题 第三讲 多重背包问题 第四讲 混合三种背包问题 第五讲 二维费用的背包问题 第六讲 分组的背包问题 第七讲 有依赖的背包问题 ...
- (背包dp) 背包N讲
文章目录 前言 相关练习题 模板题 01背包 完全背包 多重背包 小数据范围 (可朴素暴力) 中等数据范围 (二进制优化) 大数据范围 (单调队列优化) 混合背包 二维费用背包 分组背包 有依赖的背包 ...
- 【转】《背包九讲》--崔添翼大神
背包九讲 目录 第一讲 01背包问题 第二讲 完全背包问题 第三讲 多重背包问题 第四讲 混合三种背包问题 第五讲 二维费用的背包问题 第六讲 分组的背包问题 第七讲 有依赖的背包问题 第八讲 泛化物 ...
- 背包九讲之一:01背包问题
文章目录 说明: 01背包问题 题目 基本思路 初始化的细节问题 优化空间复杂度 相关题目练习 题目URL 输入格式 输出格式 数据范围 输入样例 输出样例: 题目解法 说明: 本文所讲内容摘录自崔添 ...
- 转载——背包九讲(原文链接已不可考)
浙大崔添翼对背包问题的讲解,观点很高也很深刻,特此转载. 背包九讲 目录 第一讲 01背包问题 第二讲 完全背包问题 第三讲 多重背包问题 第四讲 混合三种背包问题 第五讲 二维费用的背包问 ...
- 背包九讲之二:完全背包问题
文章目录 说明: 完全背包问题 题目 基本思路 一个简单有效的优化 相关题目练习 题目URL 输入格式 输出格式 数据范围 输入样例 输出样例: 题目解法 说明: 本文所讲内容摘录自崔添翼:背包九讲, ...
- Backpack-problem背包九讲笔记
背包问题 1 01背包 1.1题目 有NNN件物品和一个容量为VVV的背包,放入第iii件物品耗费的费用是CiC_iCi,得到的价值是WiW_iWi.求解将哪些物品放入背包可使价值总和最大. ex ...
最新文章
- 初学编程的小白 | 每日趣闻
- 笑傲江湖霍建华版电子相册
- 什么是可重入函数和不可重入函数
- 大话PM | 产品经理必备利器——UML
- Kafka学习 之 第一个例子(一)
- linux和用户账户相关的系统文件
- 计算机高职考理论知识,考计算机基础知识理论试题
- hdu 5312 数学
- host 端口_如何让多端口网站用一个nginx进行反向代理实际场景分析
- struts2 从一个action 跳转到另一个action的两种方法
- java拦截器要怎么写_SpringMVC中的拦截器详解及代码示例
- P2463 [SDOI2008]Sandy的卡片
- 浪涌保护器ant120_ANT120/530/1P浪涌保护器服务周到漳州
- 计算机化学试题,08计算机化学试卷yuanj.doc
- k620显卡 unreal_nvidia quadro k620是什么显卡
- 互联网公司愚人节策划大盘点,在恶搞界谁是老大?
- 共享网络隐藏计算机,把电脑变成无线路由器,wifi共享大师隐藏ssid-
- GDP越高就越幸福吗?用Python分析《世界幸福指数报告》后我们发现…
- Unnamed System Edition v4.0
- 派大星如期反馈小程序的生命周期
热门文章
- 计算机视觉方向简介 | 深度学习视觉三维重建
- ML基础 : 训练集,验证集,测试集关系及划分 Relation and Devision among training set, validation set and testing set...
- 团队-团队编程项目作业名称-模块开发过程
- java-第十一章-类的无参方法-计算器运算
- PHP:第一章——PHP中的魔术常量
- MaterialIDsRandomGenerator for 3dsMax使用教程
- nginx参数优化杂记
- iis7.5配置.net mvc注意事项
- (备忘)Java数据类型中String、Integer、int相互间的转换
- 大华嵌入式硬盘录像机数据恢复工具