2023-09-25

背包问题全解

1简单背包

我们通常使用一个二维数组表示这个背包的内容状态,比如f[num][weight]对应一定数量与重量的背包内容,然后遍历,从背包满载开始倒推。这里我们先采用二维DP的方式,虽然但是有的时候会引起MLE

设 DP 状态 dp[i][j]为在只能放前i个物品的情况下,容量为j的背包所能达到的最大总价值。

考虑转移。假设当前已经处理好了前 i-1个物品的所有状态,那么对于第i个物品,当其不放入背包时,背包的剩余容量不变,背包中物品的总价值也不变,故这种情况的最大价值为 f[i-1][j];当其放入背包时,背包的剩余容量会减小w[i] ,背包中物品的总价值会增大c[i],故这种情况的最大价值为 f[i-1][j-w[i]]+c[i]。

#include<cstdio>

using namespace std; const int maxm=201,maxn=31; int m,n; int w[maxn],c[maxn];//weight and cost int f[maxn][maxm];//f[数量][质量] int max(int x,int y){ \tif(x<y)return y; \telse return x; } int main(){ \tscanf("%d%d",&m,&n); \tfor(int i=1;i<=n;i++) \t\tscanf("%d%d",&w[i],&c[i]); \tfor(int i=1;i<=n;i++) \t\tfor(int v=m;v>0;v--) \t\t\tif(w[i]<=v/可以放包里/)f[i][v]=max(f[i-1][v],f[i-1][v-w[i]]+c[i]);//不断推导当前数目放东西收益最大 \t\t\telse f[i][v]=f[i-1][v]; \tprintf("%d",f[n][m]); \treturn 0;/10 4 \t2 1 \t3 3 \t4 5 \t7 9/ }

二维背包比较碍事,而且fi只跟fi-1有关,所以可以去除一维

#include<cstdio>

using namespace std; const int maxm=2001,maxn=31; int m,n; int w[maxn],c[maxn];//weight & cost int f[maxm]; int main(){ \tscanf("%d%d",&m,&n); \tfor(int i=1;i<=n;i++) \t\tscanf("%d%d",&w[i],&c[i]); \tfor(int i=1;i<=n;i++) \t\tfor(int v=m;v>=w[i];v--)//只要放得下即可,并且不断压缩至正好 \t\t\tif(f[v-w[i]]+c[i]>f[v])f[v]=f[v-w[i]]+c[i]; \tprintf("%d",f[m]); \treturn 0; \t/10 4 \t2 1 \t3 3 \t4 5 \t7 9/ }

在v的循环反向是为了不多放物体,因为0-1背包只能放一个

2完全背包

而与之相对应的完全背包则更加丰富,可以填充无数的同一个物体,代码实现如下:

#include<cstdio>

using namespace std; const int maxm=201,maxn=31; int m,n; int w[maxn],c[maxn]; int f[maxm]; int main(){ \tscanf("%d%d",&m,&n); \tfor(int i=1;i<=n;i++) \t\tscanf("%d%d",&w[i],&c[i]); \tfor(int i=1;i<=n;i++) \t\tfor(int v=w[i];v<=m;v++)//可以逐渐填入一个w[i],两个w[i]······ if(f[v-w[i]]+c[i]>f[v])f[v]=f[v-w[i]]+c[i]; printf("%d
",f[m]); return 0;/10 4 2 1 3 3 4 5 7 9/ }

#include<cstdio>

using namespace std; const int maxm=201,maxn=31; int m,n; int w[maxn],c[maxn]; int f[maxm]; int main(){ \tscanf("%d%d",&m,&n); \tfor(int i=1;i<=n;i++) \t\tscanf("%d%d",&w[i],&c[i]); \tfor(int i=1;i<=n;i++) \t\tfor(int v=w[i];v<=m;v++)//可以逐渐填入一个w[i],两个w[i]······ if(f[v-w[i]]+c[i]>f[v])f[v]=f[v-w[i]]+c[i]; printf("%d
",f[m]); return 0;/10 4 2 1 3 3 4 5 7 9/ }

3多重背包

多重背包的特别之处,在于对每个物品施加了个数的限制,相比于0-1背包又可以多选物品,所以相比之下状态转移方程更加丰富。

#include<cstdio>

using namespace std; int v[6002],w[6002],c[6002]; int f[6002]; int n,m; int max(int x,int y){ \tif(x<y)return y; \telse return x; } int main(){ \tscanf("%d%d",&n,&m); \tfor(int i=1;i<=n;i++) \t\tscanf("%d%d%d",&v[i],&w[i],&c[i]); \tfor(int i=1;i<=n;i++) \t\tfor(int j=m;j>=0;j--) \t\t\tfor(int k=0;k<=c[i];k++) \t\t\t{\tif(j-kv[i]<0)break; f[j]=max(f[j],f[j-kv[i]]+k*w[i]); } printf("%d",f[m]); return 0; } /5 1000 80 20 4 40 50 9 30 50 7 40 30 6 20 20 1/

不如完全背包,这样的多重背包只能采取枚举,复杂度无法再优化

4一些优化:

+ 二进制优化

所以我们可以通过二进制分组的方式使得拆分方式更优美省时

所以问题由此转换成0-1背包,解决即可。

index = 0;

for (int i = 1; i <= m; i++) { int c = 1, p, h, k; cin >> p >> h >> k; while (k > c) { k -= c; list[++index].w = c * p; list[index].v = c * h; c *= 2; } list[++index].w = p * k; list[index].v = h * k; }

其他优化好复杂先不写了

5.混合背包

混合背包就是将前面三种的背包问题混合起来,有的只能取一次,有的能取无限次,有的只能取k次。

这种题目看起来很吓人,可是只要领悟了前面几种背包的中心思想,并将其合并在一起就可以了。下面给出伪代码:

for (循环物品种类) {

if (是 0 - 1 背包) 套用 0 - 1 背包代码; else if (是完全背包) 套用完全背包代码; else if (是多重背包) 套用多重背包代码; }

6.二维背包

仍然是0-1背包问题,不过要增加一维存储新的价值

洛谷大佬题单

特点:码量较少 (目前只有一题超过1KB)且难度较低 ,让新手也可以享受切题的快乐。

没学过背包问题的可以去看看洛谷日报中的背包问题或者背包九讲。

题目:

题目 题目解法 题目难度 实际难度
P1048 采药 01背包 普及- 普及-
P2871 [USACO07DEC]Charm Bracelet S 01背包 普及- 普及-
P1049 装箱问题 01背包 稍微变形 普及- 普及-
P1060 开心的金明 01背包 稍微变形 普及- 普及-
P1164 小A点菜 01背包 稍微变形 普及- 普及-
P1510 精卫填海 01背包 稍微变形 普及- 普及-
P2639 [USACO09OCT]Bessie's Weight Problem G 01背包 稍微变形 普及- 普及-
P2925 [USACO08DEC]Hay For Sale S 01背包 稍微变形 普及- 普及-
P1926 小书童——刷题大军 01背包 变形 普及- 普及-
P1802 5倍经验日 01背包 变形 普及- 普及-
P1734 最大约数和 01背包 变形 普及- 普及-
P2392 kkksc03考前临时抱佛脚 01背包 变形 普及- 普及-
P1466 [USACO2.2]集合 Subset Sums 01背包 变形 普及/提高- 普及-
P2370 yyy2015c01 的 U 盘 01背包 变形 普及/提高- 普及/提高-
CF294B Shaass and Bookshelf 01背包 变形 普及+/提高 普及/提高-
CF19B Checkout Assistant 01背包 变形 提高+/省选- 普及/提高-
P4141 消失之物 01背包 变形 提高+/省选- 普及+/提高
P1156 垃圾陷阱 01背包 变形 提高+/省选- 普及+/提高
P1507 NASA的食物计划 二维01背包 普及- 普及-
P1910 L国的战斗之间谍 二维01背包 普及- 普及-
P1855 榨取kkksc03 二维01背包 稍微变形 普及- 普及-
P1877 [HAOI2012]音量调节 二维01背包 变形 普及- 普及-
P1509 找啊找啊找GF 二维01背包 变形 普及/提高- 普及/提高-
P3985 不开心的金明 二维01背包 变形 普及+/提高 普及/提高-
P1455 搭配购买 01背包 +并查集 普及/提高- 普及/提高-
P1941 飞扬的小鸟 01背包 变形+完全背包 变形 普及+/提高 普及+/提高
P2170 选学霸 01背包 变形+并查集 普及+/提高 普及+/提高
P1858 多人背包 01背包 优解变形 提高+/省选- 普及+/提高
P1616 疯狂的采药 完全背包 普及- 普及-
P2722 [USACO3.1]总分 Score Inflation 完全背包 普及- 普及-
P1679 神奇的四次方数 完全背包 变形 普及- 普及-
P1832 A+B Problem(再升级) 完全背包 变形 普及- 普及-
P1853 投资的最大效益 完全背包 变形 普及/提高- 普及-
CF189A Cut Ribbon 完全背包 变形 普及/提高- 普及-
CF417A Elimination 完全背包 变形 普及/提高- 普及-
P2918 [USACO08NOV]Buying Hay S 完全背包 变形 普及/提高- 普及/提高-
P5662 纪念品 完全背包 变形 普及+/提高 普及/提高-
P5020 [NOIP2018 提高组] 货币系统 完全背包 变形 普及+/提高 普及+/提高
P2347 砝码称重 多重背包 变形 普及- 普及-
P6771 [USACO05MAR]Space Elevator 太空电梯 多重背包 变形 普及/提高- 普及/提高-
P5365 [SNOI2017]英雄联盟 多重背包 变形 普及+/提高 普及/提高-
P1776 宝物筛选 多重背包 二进制优化 普及+/提高 普及/提高-
P2851 [USACO06DEC]The Fewest Coins G 多重背包 +完全背包 提高+/省选- 普及/提高-
P1833 樱花 混合背包 普及+/提高 普及/提高-
P1782 旅行商的背包 混合背包 二进制优化+完全背包+卡常 提高+/省选- 普及+/提高
P1757 通天之分组背包 分组背包 普及- 普及/提高-
P5322 [BJOI2019]排兵布阵 分组背包 变形 提高+/省选- 普及/提高-
P1064 金明的预算方案 有依赖的背包 普及+/提高 普及/提高-
P2904 [USACO08MAR]River Crossing S 背包思想 普及/提高- 普及/提高-

最后:

1.如题目难度不符合实际,补充的题目或修改建议欢迎私信。

2.这个题单的题应该算比较简单,以后再写一个难一点的。

3.写题单不易,能不能帮忙收藏一下啊。

another 借鉴

27000字初级背包问题详解 - walker shi的文章 - 知乎

https://zhuanlan.zhihu.com/p/430195885