背包DP学习笔记
01背包
由于01背包太过经典,所以一定要把每一个细节理解透彻!
有
个物品和一个容量为 的背包,每个物品有体积 和价值 ,求用这个背包所能装下的最大价值。
设
接下来,我们再考虑一些细节。对于
然后就到了经典的空间优化:我们可以发现
接下来还有一个经典的常数优化:因为当
所以,我们就得到了经典的01背包代码:
for(int i=1;i<=n;i++)
for(int j=m;j>=w_i;j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
cout<<f[m]<<endl;
例题:采药
正好填满的01背包
是一个01背包经典的变形,题意基本与01背包相同,但要求背包必须填满。
这时,
但是,仔细推理一下,就会发现,状态转移方程和01背包一模一样,空间优化和常数优化也都通用。那不一样的地方在哪里呢?答案是初始化。由于要求体积恰好为
代码:
memset(f,-0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++)
for(int j=m;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
cout<<f[m]<<endl;
二维费用背包
有两维费用(如:一个事件既要消耗时间也要消耗金钱,获得一定价值)的01背包。
将01背包多开两维费用,其他完全相同。
代码:
//背包第一维容量为m,背包第二维容量为t
for(int i=1;i<=n;i++)
for(int j=m;j>=w1[i];j--)
for(int k=t;k>=w2[i];k--)
f[j][k]=max(f[j][k],f[j-w1[i]][k-w2[i]]+v[i]);
cout<<f[m][t]<<endl;
例题:榨取kkksc03
完全背包
又是一个经典模型,也必须要理解透彻。题意与01背包基本相同,但每个物品能选无数遍。
同样设
同01背包一样,我们也可以省略掉
代码:
for(int i=1;i<=n;i++)
for(int j=w[i];j<=m;j++)
f[j]=max(f[j],f[j-w[i]]+v[i]);
cout<<f[m]<<endl;
例题:疯狂的采药
多重背包
题意与01背包基本相同,但每种物品能选
一个很容易想到的思路为将一种物品选
这样的复杂度显然不够优秀,所以我们考虑优化。我们希望将每个物品选
代码:
for(int i=1;i<=n;i++)
for(int tmp=1;x[i];tmp*=2) {
int num=min(tmp,x[i]);
int wt=w[i]*num,vt=v[i]*num;
for(int j=m;j>=wt;j--)
f[j]=max(f[j],f[j-wt]+vt);
x[i]-=num;
}
cout<<f[m]<<endl;
例题:宝物筛选
混合背包
将01背包、完全背包和多重背包缝合在一起的问题。
思路很简单,无需多讲解,分别考虑即可。形式为:
for(枚举物品) {
if(01背包)
01背包代码
else if(完全背包)
完全背包代码
else
多重背包代码
}
实际上,01背包和多重背包可以共用多重背包的代码,因为01背包可以当成只能取一次的多重背包。
例题:樱花
核心代码:
for(int i=1;i<=n;i++) {
if(x[i]==0) {//完全背包
for(int j=w[i];j<=m;j++)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
else {//01背包和多重背包
for(int tmp=1;x[i];tmp*=2) {
int num=min(tmp,x[i]);
int wt=w[i]*num,vt=v[i]*num;
for(int j=m;j>=wt;j--)
f[j]=max(f[j],f[j-wt]+vt);
x[i]-=num;
}
}
}
cout<<f[m]<<endl;
至此,基本模型已经讲完,其他变种模型以后有空再更新。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步