背包可以分为六大类:最基本的0/1背包、完全背包、多重背包、分组背包、混合背包和二维费用的背包。不管是什么类型的背包问题,最后都可以转化为0/1背包来做
0/1背包
板子题:有n件物品和一个容积为v的背包,第i件物品的体积为c[i],价值为w[i],求在不超过容积的情况下能装入物品的最大价值
当用二维的f数组进行状态转移时,f[i][j]表示前i件物品放入容积为j的空间内取得的最大价值(注意前这个字 背包其实是资源分配中的一个分支),那么就可以得到状态转移方程:
f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i])
f[i-1][j]表示不取这件物品,f[i-1][j-c[i]]+w[i]表示取
代码如下:
for (int i=1;i<=n;i++)
for (int j=0;j<=v;j++)
{
f[i][j]=f[i-1][j];
if (j>=c[i])
f[i][j]=max(f[i][j],f[i-1][j-c[i]]+w[i]);
}
或者可以用一维的f数组来进行状态转移,可以将空间复杂度优化为O(v),时间复杂度仍是O(n*v)
代码如下:
for (int i=1;i<=n;i++)
for (int j=v;j>=0;j--)//变为反向搜,因为个数限制,在完全背包中就是0 to v do
{
if (j>=c[i])
f[j]=max(f[j],f[j-c[i]]+w[i]);
}
完全背包
板子题:即0/1背包的板子题,不同的是0/1背包每件物品只有一件,完全背包每种物品都有无数件可以取
算法1:将其转化为0/1背包做
二维的状态转移方程为:f[i][j]=max(f[i-1][j-k*c[i]]+k*w[i])
k*c[i]∈[0,j]
这种算法的时间复杂度比较高,所以有了第二种算法:
将0/1背包一维算法中的j层循环变为从0到v
在0/1背包中为了保证每件物品采用数量,所以要倒着搜,而完全背包中没有对数量的限制,所以j为0到v
多重背包
板子题:逃亡的准备
多重背包的每种物品有固定的数量a[i]件可以取,这个时候就要用一手二进制法(名字高端是不是)
对于一个数,比如10,可以将它分为1+2+4+3
这四个数可以组成1-10之间的每个数字(不信你可以试试),那么如果一种物品有10件可以取,就将其分为1,2,3,4的四种0/1背包的物品,最后我们还是把这一类型的背包转化为了0/1背包
代码如下:
cin >>n >>C;
for (int i=1;i<=n;i++)
cin >>a[i] >>w[i] >>v[i];//分别是数量、重量和价值
for (int i=1;i<=n;i++)
{
if (w[i]*a[i]>C)//在物品足够多的时候,对其做完全背包
{
for (int j=0;j<=C;j++)
{
if (j>=w[i])
c[j]=max(c[j],c[j-w[i]]+v[i]);
}
}
else
{
int k=1,num=a[i];
while(k<num)//是否取一个重量为k*w[i],价值为k*v[i]的物品
{
for (int j=C;j>=k*w[i];j--)
c[j]=max(c[j],c[j-k*w[i]]+k*v[i]);
num-=k;
k+=k;
}
for (int j=C;j>=num*w[i];j--)//剩下的作为单独的一个物品计算
c[j]=max(c[j],c[j-num*w[i]]+num*v[i]);
}
}
混合背包
将其每一种物品用对应的算法做。。。
分组背包
板子题:在0/1背包的基础上,将每个物品分为了k组,每组中的物品会冲突,只能取一件
设f[k][c]为前k组物品装入c时取得的最大价值
所以可以得到状态转移方程:
f[k][c]=max(f[k-1][c],f[k-1][c-w[i]]+v[i])
贴代码:
for (int k=1;k<=K;k++)
{
for (int j=C;j>=0;j--)
{
i=每组k内所有物品
{
if (j>=v[i])
f[j]=max(f[j],f[j-c[i]+w[i]);
}
}
}
推荐题:金明的预算方案
其实这题用分组背包是一种方法,正规方法应该是用树形DP做(多捞哦)
将每个主件,主件+附件1,主件+附件2,主件+附件1+附件2分为一组,套上分组背包的模版
下面感谢MHT提供的代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=40000;
int f[300][maxn];
int q[100][3];
int p[100][3];
int m,n;
int main()
{
int x,y,z;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>x>>y>>z;
if(z==0)
{
q[i][0]=x;
p[i][0]=y;
}
else
{
if(!q[z][1])
{
q[z][1]=x;
p[z][1]=y;
}
else
{
q[z][2]=x;
p[z][2]=y;
}
}
}
memset(f,0,sizeof(f));
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
f[i][j]=f[i-1][j];
if(j>=q[i][0])
{
f[i][j]=max(f[i][j],f[i-1][j-q[i][0]]+p[i][0]*q[i][0]);
}
if(j>=q[i][0]+q[i][1])
{
f[i][j]=max(f[i][j],f[i-1][j-q[i][0]-q[i][1]]+p[i][0]*q[i][0]+p[i][1]*q[i][1]);
}
if(j>=q[i][0]+q[i][2])
{
f[i][j]=max(f[i][j],f[i-1][j-q[i][0]-q[i][2]]+p[i][0]*q[i][0]+p[i][2]*q[i][2]);
}
if(j>=q[i][0]+q[i][1]+q[i][2])
{
f[i][j]=max(f[i][j],f[i-1][j-q[i][0]-q[i][1]-q[i][2]]+p[i][0]*q[i][0]+p[i][1]*q[i][1]+p[i][2]*q[i][2]);
}
}
cout<<f[m][n];
return 0;}
有依赖的背包问题
留坑哈哈哈哈哈哈哈