背包题型总结
概述
大致分为以下几类:
- 01背包
- 完全背包
- 多重背包
- 混合背包
- 二维背包
- 分组背包
- 依赖背包(树形背包)
以及一个变式:跳楼梯模型,本质是转移顺序的改变。
背包题基本上没有套路,只能靠多练题,总结思路,最重要的还是把问题抽象成背包模型的能力。
01 背包
特点:无序加入,每个物品加一次。
完全背包
特点:无序加入,每个物品无限加。
变式:跳楼梯模型:问跳完一段楼梯有多少种不同的方案数。
这两者的区别就在于:
- 跳楼梯模型只有求方案数时才可以使用,而完全背包既可以求方案数,也可以求最优解。(完全背包除了求方案数以外,求最优解其实可以使用跳楼梯模型,但是这种写法不普遍也不推荐。)
- 跳楼梯模型强调有序 ,即要考虑谁先放谁后放;而完全背包强调无序,两个物品先放后放不会影响最终答案。
- 转移顺序不同。
第三点的区别最主要体现在代码上:
完全背包是外层先循环物品种类,然后循环背包容量。这样就可以通过人为地定一个放的顺序来达到无序的效果。
跳楼梯模型是外层先循环背包容量,然后循环物体种类。这样就可以使后放的情况包含先放的情况。
具体区别可以看这两个题,很明显:
多重背包
特点:无序加入,一个物体有多个。
重点在于其优化方式以及看出题目中各类物品对应题目中的什么东西。
朴素做法时间为
优化方式
二进制优化
约定:
正确的原因(本质):任何数都可以用二进制表示出来,因此我们挑选出二的整数次幂数量的物体,依靠他们组合便能组合出所有数量物体。
实现:把一个物体按上述方式拆分成多个物体,进行 01 背包。
细节:一个物体,从
时间复杂度
单调队列优化
约定:
正确的原因(本质):观察到
我们发现,第二维的数在模
并且
那么我们对于每一个剩余系,做一个单调队列,来维护
需要注意的是:这里我们要做一个 dp 数组的备份,因为我们是顺序转移的,无法保证这次的转移一定是转上一次的,因此我们要先把上一次的 dp 数组存下来。
具体实现:第一维枚举每个数,第二维枚举模
时间复杂度分析:第一维枚举
板子题代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[40005],n,v,w,m,s,q[100005],h,t,g[40005];
int main()
{
cin>>n>>s;
while(n--)
{
cin>>v>>w>>m;//v:价格 , w:重量 , m:数量
for(int i=0;i<=s;i++)
{
g[i]=f[i];
}
for(int i=0;i<w;i++)
{
h=0,t=-1;
for(int j=i;j<=s;j+=w)
{
while((t-h+1)&&q[h]<j-m*w)h++;
if((t-h+1))f[j]=max(g[j],g[q[h]]+(j-q[h])/w*v);
while((t-h+1)&&(g[j]>=g[q[t]]+(j-q[t])/w*v))t--;//要加上这次放入的价值再判断弹出队尾
q[++t]=j;
}
}
}
cout<<f[s];
return 0;
}
依赖背包(树形背包)
特殊情况的做法:规定树只有两层
穷举:金明的预算方案
对于只有两层,且儿子节点的数量极少的情况,我们可以将一个树内的选法穷举出来,做分组背包。
时间是
一层分组
对于主件和附件依赖的情况,我们对依赖某个特定主件的所有附件做一个 01 背包,算出该子树内 最大体积为
时间
树形背包通解
观察上面 “一层分组” 的做法,很容易想到,我们对树中的每个子树都做一次这样的操作就好了。
时间为
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战