算法核心:
首先,需要设置一个二维数组t[][],其中t[i][j]表示利用前i个物品来装进容量为j的背包的所能够获得的最大价值。
当只考虑第i件物品时,可将情况分为是否放入第i件物品两种:
1.01背包——每个物品仅有一个
不放第i件物品:t[i][j]=t[i-1][j]
放入第i件物品:t[i][j]=t[i-1][j-weights[i]]+values[i]
2.完全背包——每个物品有无数个
不放第i件物品:t[i][j]=t[i-1][j]
放入第i件物品:t[i][j]=t[i][j-weights[i]]+values[i]
3.多重背包——每个物品数量有限
不放第i件物品:t[i][j]=t[i-1][j]
放入第i件物品:t[i][j]=t[i-1][j-k*weights[i]]+k*values[i]
注意:
(1)放入第i件物品的前提是当前空间j>=放入的物品的空间,即(j-weights[i]>=0或者j-k*weights[i]>=0);
(2)多重背包问题放入和不放入可以直接合为一种情况,即k的取值多一个k=0;
(3)对于t[][]数组一定要合理合适正确的进行初始化。对于找数值最大的问题,直接全部初始化为0即可;但是对于找数值最小的问题,在最开始需要将t数组全部初始化为一个超大值,然后并将t[0][0]初始为0;
(4)在这类题中,若有n类物品,一般从i=1开始进行对应,所以for循环一般为for(i=1;i<=n;i++);另一层表示空间的循环为for(j=0;j<=s;j++)。
实例以及代码
1.问题:有数量有限的几种面值的货币,要求用最少的张数凑成一个给定的总值。
2.实现代码
#define m 10000 //用于初始化数组
int t[30][30]={0},n,space; //n是种类,space是目标值
int weights[10];//面值
int values[10];//每一张的价值
int numbers[10];//每一种面值对应的数量
//处理输入
void Input(){
int i;
printf("纸币的种类数:");
scanf("%d",&n);
printf("对应纸币的面值:");
for(i=1;i<=n;i++)
scanf("%d",&weights[i]);
printf("对应纸币的张数:");
for(i=1;i<=n;i++)
scanf("%d",&numbers[i]);
for(i=1;i<=n;i++)
values[i]=1;
printf("期望凑的总面值:");
scanf("%d",&space);
}
//数组t[][]初始化,这里是找最小值,于是往大了初始化
void Initialize(){ //全部初始化,只保留t[0][0]
int i,j;
for(i=0;i<=n;i++)
for(j=0;j<=space;j++)
t[i][j]=m;
t[0][0]=0;
}
//01背包
void ZeroOneBackpack(){
int i,j,temp1,temp2=100;
for(i=1;i<=n;i++)
for(j=0;j<=space;j++)
{
temp1=t[i-1][j];//第i个不放
if(j-weights[i]>=0) //第i个放
temp2=t[i-1][j-weights[i]]+values[i];
if(temp1<temp2)
t[i][j]=temp1;
else
t[i][j]=temp2;
}
}
//完全背包
void CompletelyBackpack(){
int i,j,temp1,temp2=100;
for(i=1;i<=n;i++)
for(j=0;j<=space;j++)
{
temp1=t[i-1][j];//第i个不放
if(j-weights[i]>=0) //第i个放
temp2=t[i][j-weights[i]]+values[i];
if(temp1<temp2)
t[i][j]=temp1;
else
t[i][j]=temp2;
}
}
//多重背包
void MultipleBackpack(){
int i,j,temp,k,min;
for(i=1;i<=n;i++)
for(j=0;j<=space;j++) //j也从0开始
{
min=m;
for(k=0;k<=numbers[i];k++)
if(j-k*weights[i]>=0) //记得可以等于
{
temp=t[i-1][j-k*weights[i]]+k*values[i];
if(temp<min)
min=temp;
}
t[i][j]=min;
}
}
//输出结果
void Output(){
printf("最小的凑齐张数:%d\n",t[n][space]);
}
int main(){
Input();
//每次都要初始化一下数组t[][]
Initialize();
ZeroOneBackpack();
printf("0-1背包");
Output();
Initialize();
CompletelyBackpack();
printf("完全背包");
Output();
Initialize();
MultipleBackpack();
printf("多重背包");
Output();
return 0;
}
3.运行结果