1、0-1揹包问题&POJ3624
问题:有N个物品,揹包容量为C,第i个物品的重量为wi、价值为vi。选择几个物品放入揹包,要求重量和不超过C,求最大价值。
用二维数组bag[N][C]存储,bag[i][j]表示在前i个物品下,容量为j时的最大价值,当i=N,j=C时即为题解
状态转移方程:bag[i][j]=wi>j?bag[i-1][j]:max(bag[i-1][j],bag[i-1][j-wi]+vi)
注意1:要考虑边界值,较简单
注意2:可优化储存,数组定义为bag[2][C],两行反复用即可,此时没有边界值问题
POJ3624即为简单揹包问题,为避免存储越界,使用注意二
#include<stdlib.h>
#include<stdio.h>
int bag[2][12881]={0};
int main(){
int n,c,wi,vi;
scanf("%d %d",&n,&c);
int k;
int x1=0,x2=1,t;
while(n--)
{
scanf("%d %d",&wi,&vi);
for(k=1;k<=c;k++)
{
if(wi>k)
{
bag[x2][k]=bag[x1][k];
}else
{
bag[x2][k]=bag[x1][k]>(bag[x1][k-wi]+vi)?bag[x1][k]:(bag[x1][k-wi]+vi);
}
}
t=x1;x1=x2;x2=t;
}
printf("%d",bag[x1][c]);
return 0;
}
2、完全揹包问题&POJ1384
与01揹包的不同点在于,完全揹包的物品可重复使用,而01揹包只能使用一次。只需稍加修改状态转移方程,其他类似。这里依然使用只有两行的二维数组,改成以为数组也类似。
状态转移方程:bag[i][j]=max{bag[i][j-w]+v,bag[i-1][j]}
if(j-w>=0)
{
bag[x2][j]=bag[x2][j-w]+p;
}
bag[x2][j]=bag[x2][j]<bag[x1][j]?bag[x2][j]:bag[x1][j];
注意:状态转移方程与01揹包的区别,01揹包:取[i-1][j-w]+v和[i-1][j]最大值,而完全揹包:取[i][j-w]+v和[i-1][j]最大值(必须正向循环)
POJ1384为完全揹包问题
#include<stdlib.h>
#include<stdio.h>
int bag[2][10001]={0};
int inf=1e9;
int main()
{
int t;
int e,f,fe;
int n;
int w,p;
int i,j;
int x1=0,x2=1,temp;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&e,&f);
fe=f-e;
for(j=1;j<=fe;j++)
{
bag[0][j]=inf;
bag[1][j]=inf;
}
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d %d",&p,&w);
for(j=1;j<=fe;j++)
{
if(j-w>=0)
{
bag[x2][j]=bag[x2][j-w]+p;
}
bag[x2][j]=bag[x2][j]<bag[x1][j]?bag[x2][j]:bag[x1][j];
}
temp=x1;x1=x2;x2=temp;
}
if(bag[x1][fe]<inf)
{
printf("The minimum amount of money in the piggy-bank is %d.\n",bag[x1][fe]);
}else
{
printf("This is impossible.\n");
}
}
}
3、01揹包与完全揹包的完整代码
#include <stdio.h>
#include <stdlib.h>
int bag[2][100]={{0}};
int main()
{
int n,c;
int w,v;
int i;
int x1=0,x2=1,t;
scanf("%d %d",&n,&c);
while(n--)
{
scanf("%d %d",&w,&v);
for(i=1;i<=c;i++)
{
if(i>=w)
{
//只差这一段代码
//01揹包
//bag[x2][i]=bag[x1][i-w]+v>bag[x1][i]?bag[x1][i-w]+v:bag[x1][i];
//完全揹包
//bag[x2][i]=bag[x2][i-w]+v>bag[x1][i]?bag[x2][i-w]+v:bag[x1][i];
}else
{
bag[x2][i]=bag[x1][i];
}
}
printf("\n");
t=x1;x1=x2;x2=t;
}
printf("%d",bag[x1][c]);
return 0;
}
4、多重揹包
多重揹包就是限制数量的完全揹包,每种物品取有限个。
设第i层数量为ci
1、类似完全揹包的方法,内层循环直接循环ci次,顺序执行,每次与上一层比较(完全揹包是同本层比较),不过这种方法时间复杂度过高。
2、类似01揹包的方法,把ci个相同的物品组合成几个不同的物品,然后当作01揹包即可。拆分的方法是,ci=1+2+4+8+…+剩余,如8=1+2+4+1,15=1+2+4+8,这样可以组合为1~ci的所有值。
hdu2191即为多重揹包
#include <stdio.h>
#include <stdlib.h>
int main()
{
int bag[101];
int t;
int m,n;
int w,v,c;
int i,j,k;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&m,&n);
for(i=0;i<=m;i++)
{
bag[i]=0;
}
for(i=0;i<n;i++)
{
scanf("%d %d %d",&w,&v,&c);
int sum=0;
for(k=1;;)
{
for(j=m;j>=1;j--)
{
if(k*w<=j)
{
bag[j]=bag[j-k*w]+v*k>bag[j]?bag[j-k*w]+v*k:bag[j];
}
}
sum+=k;
if(sum==c)
{
break;
}else if(sum+k*2>c)
{
k=c-sum;
}else
{
k*=2;
}
}
}
printf("%d\n",bag[m]);
}
return 0;
}
poj1742,还是转01揹包的做法,要加上剪枝,不然会超时。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n,m;
int bag[100001];
int an[100],cn[100];
int i,j,k;
int w;
bag[0]=1;
while(scanf("%d %d",&n,&m)!=EOF&&n&&m)
{
for(i=1;i<=m;i++)
{
bag[i]=0;
}
for(i=0;i<n;i++)
{
scanf("%d",&an[i]);
}
for(i=0;i<n;i++)
{
scanf("%d",&cn[i]);
}
for(i=0;i<n;i++)
{
if(an[i]*cn[i]>=m)
{
w=an[i];
for(j=w;j<=m;j++)
{
if(!bag[j])
{
bag[j]=bag[j-w];
}
}
}else
{
int sum=0;
for(k=1;;)
{
w=an[i]*k;
for(j=m;j>=w;j--)
{
if(!bag[j])
{
bag[j]=bag[j-w];
}
}
sum+=k;
if(sum==cn[i])
{
break;
}else if(sum+k*2>cn[i])
{
k=cn[i]-sum;
}else
{
k*=2;
}
}
}
}
int num=0;
for(i=1;i<=m;i++)
{
if(bag[i]) num++;
}
printf("%d\n",num);
}
return 0;
}