最近正好在做spark给我布置的dp专题,借机会重新整理一下背包问题
(dp简直弱的想哭)
以下代码纯自己手写 如果有写错欢迎提醒
/* 0-1背包
for(i=1;i<=n;i++)
for(j=total;j>=v[i];j--)
{
dp[i][j]=max(dp[i][j-1],max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]));
dp[j]=max(dp[j-1],max(dp[j],d[j-v[i]]+w[i]));
}
printf("%d\n",dp[total]);
printf("%d\n",dp[n][total]);
完全背包
for(i=1;i<=n;i++)
for(j=v[i];j<=total;j++)
{
dp[i][j]=max(dp[i-1][j],max(dp[i][j-v[i]]+w[i],dp[i][j-1]));
dp[j]=max(dp[j],max(dp[j-1],dp[j-v[i]]+w[i]));
}
printf("%d\n",dp[n][total]);
printf("%d\n",dp[total]);
多重背包
struct data 定义结构体来存储已知信息
{
int v,w,num;
}a[maxn];
int top=0;
for(i=1;i<=n;i++)
scanf("%d%d%d",&a[i].v,&a[i].w,&a[i].num);
开始二进制优化把问题转化成01背包
void change()
{
for(int i=1;i<=n;i++)
{
int k=1;
while(a[i].num>=k)
{
int temp=a[i].num-k;
a[i].num-=k;
k=k*2;
v[++top]=temp*a[i].v;
w[top]=temp*a[i].w;
}
if(a[i].num!=0)
{
v[++top]=a[i].nun*a[i].v;
w[top]=a[i].num*a[i].w;
a[i].num=0;
}
}
}
for(i=1;i<=n;i++)
for(j=total;j>=v[i];j--)
{
dp[i][j]=max(dp[i][j-1],max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]));
dp[j]=max(dp[j-1],max(dp[j],d[j-v[i]]+w[i]));
}
printf("%d\n",dp[total]);
printf("%d\n",dp[n][total]);
二维费用背包
int max=-INF;
for(int i=1;i<=n;i++)
for(int j=totalv;j>=v[i];j--)
for(int l=totalw;l>=w[i];l--)
{
dp[i][j][l]=max(dp[i-1][j][l],dp[i-1][j-v[i]][l-w[i]]+r[i]);
if(dp[i][j][l]>max)
max=dp[i][j][l];
}
最后在最后在dp[0..totalv][0..totalw]范围内寻找答案。
分组背包
在这种状态设置中,容易想出以下两种阶段递推方式(以下所述都为第二和第三重循环):
1,在同一个背包容量中,对不同费用的物品进行枚举比较:
for(j=MAX;j>=1;--j) //背包容量
for(k=1;k<=m;++k) //不同费用的物品
2,在同一费用的物品中,对放在不同背包容量时计算最大价值:(该方式同《背包九讲-分组背包》中的伪代码部分)
for(k=1;k<=m;++k) //不同费用的物品
for(j=MAX;j>=1;--j) //背包容量
简略分析:
1,分析第一种递推方式的正确:
该方式即求在容量为j的背包中,选择哪一个物品可以有最大价值。
看递推方程:dp[j]=max(dp[j],dp[j-c[k]]+w[k]);(其中c[k]为k物品的费用,w[k]为价值),由于递降枚举背包容量,max比较中的dp[j]是由上一组物品决策所得,在这里将被忽略。因为就算不忽略,在本组物品中dp[j]的决策依然要取决于dp[j-c[k]]+w[k]。
而同样由于递降枚举背包容量(第二重循环),dp[j-c[k]]在本组物品中是未进行过决策的,亦即背包容量为j-c[k]时,在本组物品中是没有选择任何物品的,这可以保证对dp[j]决策时,不会多选本组中的物品。
所以方程为:
for(i=1;i<=n;i++)
for(j=total;j>=1;j--)
for(k=1;k<=m;k++)
{
if(k属于i)
{
if(j>=v[k])
dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
}
}
*/