第四十四章 动态规划——背包问题模型(一)
- 一、模型概述
- 二、模型变形
- 1、AcWing 423. 采药
- (1)问题
- (2)分析
- (3)代码
- 2、AcWing 1024. 装箱问题
- (1)问题
- (2)分析
- (3)代码
- 3、AcWing 1022. 宠物小精灵之收服
- (1)问题
- (2)分析
- (3)代码
- 4、AcWing 278. 数字组合
- (1)问题
- (2)分析
- (3)代码
- 5、AcWing 1023. 买书
- (1)问题
- (2)分析
- (3)代码
- 6、AcWing 1021. 货币系统
- (1)问题
- (2)分析
- (3)代码
一、模型概述
对于背包问题而言,我们还有很多细小的分类,比如01背包问题,完全背包问题,多重背包问题,二维费用背包问题,分组背包问题等等,这些常用的背包问题作者在之前的文章中都进行过详细地讲解。
传送门:
01背包问题
完全背包问题
多重背包问题
分组背包问题
二维费用背包问题
二、模型变形
1、AcWing 423. 采药
(1)问题
(2)分析
这道题就是一个很简单的01背包问题,这里就不过多讲解了,直接套用模型即可。
(3)代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1100;
int f[N];
int a[N],b[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d",a+i,b+i);
}
for(int i=1;i<=m;i++)
{
for(int j=n;j>=0;j--)
{
if(j>=a[i])
f[j]=max(f[j],f[j-a[i]]+b[i]);
}
}
cout<<f[n]<<endl;
return 0;
}
2、AcWing 1024. 装箱问题
(1)问题
(2)分析
这道题其实也是一个非常明显的01背包问题,只不过这道题的体积和价值是一样的,而且我们需要对题目最后的问题进行转化,最小剩余体积其实就是总体积减去能装的最大体积。
(3)代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e4+10;
int a[N],f[N];
int n,v;
int main()
{
cin>>v>>n;
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=n;i++)
{
for(int j=v;j>=0;j--)
{
if(j>=a[i])
f[j]=max(f[j],f[j-a[i]]+a[i]);
}
}
cout<<v-f[v]<<endl;
return 0;
}
3、AcWing 1022. 宠物小精灵之收服
(1)问题
(2)分析
这道题是一个很明显的二维费用背包问题,这个题目作者在之前的文章中进行过详细地讲解,如果有对该问题感到疑惑的读者可以去看一看作者之前的文章。
AcWing 1022. 宠物小精灵之收服
(3)代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1010,M=510,K=110;
int f[N][M];
int a[K],b[K];
int n,m,k;
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=k;i++)scanf("%d%d",a+i,b+i);
for(int i=1;i<=k;i++)
for(int j=n;j>=0;j--)
for(int q=m;q>=0;q--)
if(j>=a[i]&&q>b[i])
f[j][q]=max(f[j-a[i]][q-b[i]]+1,f[j][q]);
int tmp=m;
if(f[n][m])
{
while(f[n][m]&&f[n][m]==f[n][tmp])tmp--;
cout<<f[n][m]<<" "<<m-tmp<<endl;
}
else cout<<0<<" "<<tmp<<endl;
return 0;
}
4、AcWing 278. 数字组合
(1)问题
(2)分析
这道题其实看上去和我们的01背包问题是非常相似的。如果这道题我们转化为01背包问题的话,描述如下:
给很多个物品和体积,然后从中任取几个物品能够恰好填满背包的方案数。
提示到这里大家可以自己再去尝试一下。
如果没有思路的话,这道题作者之前也是讲过的,还是很详细的,大家可以去看一看:
AcWing 278. 数字组合
(3)代码
代码这里就写一个空间优化过后的代码吧。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=110000;
int a[N],f[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%d",a+i);
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=m;j>=0;j--)
if(j>=a[i])f[j]=f[j]+f[j-a[i]];
cout<<f[m]<<endl;
return 0;
}
5、AcWing 1023. 买书
(1)问题
(2)分析
这道题背后的模板比较明显,这道题考察的是完全背包问题,只不过这道题并不是让我们在众多方案 中选出一个最大值,而是得出所有符合条件的方案的总数。
(3)代码
这里写的是时间和空间均优化后的代码,如果大家不懂的话,建议去看在本篇文章开头所列出的几篇文章中的完全背包问题。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1100;
int a[N],f[N];
int main()
{
a[1]=10,a[2]=20,a[3]=50,a[4]=100;
int n;
cin>>n;
f[0]=1;
for(int i=1;i<=4;i++)
{
for(int j=0;j<=n;j++)
{
if(a[i]<=j)
f[j]+=f[j-a[i]];
}
}
cout<<f[n]<<endl;
return 0;
}
6、AcWing 1021. 货币系统
(1)问题
(2)分析
这道题很明显也是一道完全背包的简单应用问题,这里不做过多的解释了。
(3)代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=20,M=3100;
long long f[M],a[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%lld",a+i);
}
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[j]=f[j];
if(j>=a[i])
f[j]+=f[j-a[i]];
}
cout<<f[m]<<endl;
return 0;
}