ACM__01背包,完全背包,多重背包

时间:2022-08-21 09:23:51

今天写题的时候碰到了一道完全背包题,可是没有看出来,乱写了一通,浪费了一个晚上,顺便复习一下背包的知识

01背包

每种物品只能选择一次或者不选,求背包容量内的最大价值

先给出状态转移方程:

f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);

解释一下:f[i][j]表示的是前i个物品中,背包容量为j时,得到的最大价值;如果在容量为j时选择不放第i个物品,那么f[i][j]=f[i-1][j],f[i-1][j]表示前一个物品在容量j时的状态值;如果在容量为j时选择放第i个物品,那么f[i][j]=f[i-1][j-w[i]]+v[i],f[i-1][j-w[i]]+v[i]表示前一个物品得到的状态加上第i个物品的价值;自然而然,f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);

下面是二维数组的代码,自己敲了一遍,加深印象

 #include<cstdio>
int main()
{
int n,m,v[],w[],f[][];
scanf("%d %d",&m,&n);
for(int i=;i<=n;i++)
scanf("%d %d",&v[i],&w[i]);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
if(w[i]<=j)
f[i][j]=max(f[i-][j],f[i-][j-w[i]]+v[i]);
else
f[i][j]=f[i-][j];
}
}
printf("%d",f[n][m]);
return ;
}

还有一种是一维数组的方法,更能节省空间

 #include<cstdio>
int main()
{
int n,m,v[],w[],f[];
scanf("%d %d",&m,&n);
for(int i=;i<=n;i++)
scanf("%d %d",&v[i],&w[i]);
for(int i=;i<=n;i++)
{
for(int j=m;j>=;j--)
{
if(w[i]<=j)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
printf("%d",f[m]);
return ;
}

里面第二个循环为什么要倒着来,遍历到第i个物品时,f[j]应该与第i-1个物品的状态比较,如果顺着来的话,f[j]则与第i个物品的状态进行了比较,这样会出事的;

完全背包

每种物品可以选无数次,求背包容量内的最大价值

状态转移方程为:f[j]=max(f[j],f[j-w[i]]+v[i]);

贴上代码:

 #include<cstdio>
int main()
{
int n,m,v[],w[],f[];
scanf("%d %d",&m,&n);
for(int i=;i<=n;i++)
scanf("%d %d",&v[i],&w[i]);
for(int i=;i<=n;i++)
{
for(int j=w[i];j<=m;j++)
{
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
printf("%d",f[m]);
return ;
}

多重背包

每种物品能取有限次,求背包容量内的最大值

多重背包问题化作01背包问题解决,如果价值为v的物品有x个,则化成x个价值为v的物品;

#include<cstdio>
int main()
{
int n,m,v[105],w[105],f[105];
scanf("%d %d",&m,&n);
for(int i=1; i<=n; i++)
scanf("%d %d",&v[i],&w[i],&num[i]);
int k=n+1;
for(int i=1; i<=n; i++)
{
for(int j=1; j<num[i]; j++)
{
v[k]=v[i];
w[k]=w[i];
k++;
}
}
for(int i=1; i<k; i++)
{
for(int j=m; j>=1; j--)
{
if(w[i]<=j)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
printf("%d",f[m]);
return 0;
}

  再贴上今天折腾了一晚上的题:http://acm.sdibt.edu.cn/JudgeOnline/problem.php?id=2340

把数组改为全局变量竟然就A了,问了老谭,说这是,没有清零的原因,脑瓜子疼!