文章目录

  • 背包九讲
    • 一、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背包九讲(待补充,暂时前两讲)相关推荐

  1. 【算法】动态规划+“背包九讲”原理超详细讲解+常见dp问题(9种)总结

    目录 一.动态规划(DP) 二.背包九讲 (1)完全背包 P1616 疯狂的采药(完全背包) (2)01背包 滚动数组 一维数组 P1048 采药(01背包) 01背包表格图示 (3)多重背包 整数拆 ...

  2. 算法--背包九讲(详细讲解+代码)

    背包九讲 目录  第一讲 01背包问题  第二讲 完全背包问题  第三讲 多重背包问题  第四讲 混合三种背包问题  第五讲 二维费用的背包问题  第六讲 分组的背包问题  第七讲 有依赖的背包问题  ...

  3. dalao的背包九讲

    背包九讲 目录  第一讲 01背包问题  第二讲 完全背包问题  第三讲 多重背包问题  第四讲 混合三种背包问题  第五讲 二维费用的背包问题  第六讲 分组的背包问题  第七讲 有依赖的背包问题  ...

  4. (背包dp) 背包N讲

    文章目录 前言 相关练习题 模板题 01背包 完全背包 多重背包 小数据范围 (可朴素暴力) 中等数据范围 (二进制优化) 大数据范围 (单调队列优化) 混合背包 二维费用背包 分组背包 有依赖的背包 ...

  5. 【转】《背包九讲》--崔添翼大神

    背包九讲 目录 第一讲 01背包问题 第二讲 完全背包问题 第三讲 多重背包问题 第四讲 混合三种背包问题 第五讲 二维费用的背包问题 第六讲 分组的背包问题 第七讲 有依赖的背包问题 第八讲 泛化物 ...

  6. 背包九讲之一:01背包问题

    文章目录 说明: 01背包问题 题目 基本思路 初始化的细节问题 优化空间复杂度 相关题目练习 题目URL 输入格式 输出格式 数据范围 输入样例 输出样例: 题目解法 说明: 本文所讲内容摘录自崔添 ...

  7. 转载——背包九讲(原文链接已不可考)

    浙大崔添翼对背包问题的讲解,观点很高也很深刻,特此转载. 背包九讲 目录  第一讲 01背包问题  第二讲 完全背包问题  第三讲 多重背包问题  第四讲 混合三种背包问题  第五讲 二维费用的背包问 ...

  8. 背包九讲之二:完全背包问题

    文章目录 说明: 完全背包问题 题目 基本思路 一个简单有效的优化 相关题目练习 题目URL 输入格式 输出格式 数据范围 输入样例 输出样例: 题目解法 说明: 本文所讲内容摘录自崔添翼:背包九讲, ...

  9. Backpack-problem背包九讲笔记

    背包问题 1 01背包 1.1题目 有NNN件物品和一个容量为VVV的背包,放入第iii件物品耗费的费用是CiC_iCi​,得到的价值是WiW_iWi​.求解将哪些物品放入背包可使价值总和最大. ex ...

最新文章

  1. 初学编程的小白 | 每日趣闻
  2. 笑傲江湖霍建华版电子相册
  3. 什么是可重入函数和不可重入函数
  4. 大话PM | 产品经理必备利器——UML
  5. Kafka学习 之 第一个例子(一)
  6. linux和用户账户相关的系统文件
  7. 计算机高职考理论知识,考计算机基础知识理论试题
  8. hdu 5312 数学
  9. host 端口_如何让多端口网站用一个nginx进行反向代理实际场景分析
  10. struts2 从一个action 跳转到另一个action的两种方法
  11. java拦截器要怎么写_SpringMVC中的拦截器详解及代码示例
  12. P2463 [SDOI2008]Sandy的卡片
  13. 浪涌保护器ant120_ANT120/530/1P浪涌保护器服务周到漳州
  14. 计算机化学试题,08计算机化学试卷yuanj.doc
  15. k620显卡 unreal_nvidia quadro k620是什么显卡
  16. 互联网公司愚人节策划大盘点,在恶搞界谁是老大?
  17. 共享网络隐藏计算机,把电脑变成无线路由器,wifi共享大师隐藏ssid-
  18. GDP越高就越幸福吗?用Python分析《世界幸福指数报告》后我们发现…
  19. Unnamed System Edition v4.0
  20. 派大星如期反馈小程序的生命周期

热门文章

  1. 计算机视觉方向简介 | 深度学习视觉三维重建
  2. ML基础 : 训练集,验证集,测试集关系及划分 Relation and Devision among training set, validation set and testing set...
  3. 团队-团队编程项目作业名称-模块开发过程
  4. java-第十一章-类的无参方法-计算器运算
  5. PHP:第一章——PHP中的魔术常量
  6. MaterialIDsRandomGenerator for 3dsMax使用教程
  7. nginx参数优化杂记
  8. iis7.5配置.net mvc注意事项
  9. (备忘)Java数据类型中String、Integer、int相互间的转换
  10. 大华嵌入式硬盘录像机数据恢复工具