题目要求
给定n种物品和一个背包。物品i的质量为wi,其价值为vi,背包的最大载重量为C。编写一个程序,求解如何装载背包里的物品,使得装入背包的物品总价值最大。
题目分析
(1)这是一个经典的问题。解决方法有很多,一般常用方法有:动态规划,分支限界,回溯法等。在这里先介绍一种简单的方法–回溯法。
(2)如果把这n种物品的取舍状态用一个向量表示{
x1
,
x2
,…,
xi
,…,
xn
},其中
xi
只有两种可能的取值1或0。当
xi
=1是,表示将第i的物品放入背包,当
xi
=0时,表示不讲第i个物品放入背包。
(3)怎样才能找到这样的向量呢?一种比较直观的方法是应用回溯法在两棵由0-1构成的解空间树中进行查找。
为什么呢?因为由概率论的知识可知,一个n维二值向量的可能取值为
2n
种,因此它的解空间相当于根结点不同的两个n层满二叉树(满二叉树有
2n−1
个叶结点,一个叶结点代表一种结果)。所以,只要搜索这两棵树,找出向量的所有可能解,再判断这些可能的解向量是否满足0-1背包的条件,找出符合条件的解向量,问题就解决了。
可用剪枝操作减掉不可行的结点及以下分支。
注意:
因为要在解空间中价值总量最高的的可能解,而这样的解不一定只有一个 (0-1背包问题可能有很多解),因此需要在搜索0-1解空间树的过程中记录下搜索的路径,然后比较哪一(些)条路径对应的装包方案总价值最高,这一(些)条搜索路径对应的装包方案就是最终的解。但是这样做的空间复杂度较高,因为每搜索到一条可能解路径都要将它记录下来,因此空间占用量太大。
一种比较简单的方法是分两次回溯搜索解空间树,第一次搜索计算出背包可装载物品的最大价值量,第二次搜索计算出满足这个最大价值量的全部装包方案。这样做虽然有些费时,但减少了空间使用。
在实际的算法设计中,并不需要真的建立所谓的解空间树,可以通过一个递归过程模拟这个搜索过程。
算法描述如下:
int x[100];
//找到物品装包的最大价值量
knap_1(int n,int flag,int c,int *price)
{
int i,j,p;
if(isOverLoad(n,c)) return; //超过背包的装载上限C,剪枝
if(n==flag) //搜索完一条可能路径
{
p=getval(n); //获得这种装包方案的总价质量
if(*price<p) //用price存储最高值
*price=p;
return;
}
for(i=0;i<=1;i++)
{
x[n]=i; //生成可能解路径
knap_1(n+1,flag,c,price);
}
}
knap_2(int n,int flag,int c,int price)
{
int i,j,p;
if(isOverLoad(n,c)) return;
if(n==flag)
{
p=getval(n);
if(price==p)
{
输出装包方案;
return; //回溯继续搜索
}
return;
}
for(i=0;i<=1;i++)
{
x[n]=i; //生成可能解路径
knap_2(n+1,flag,c,price);
}
}
算法分析:
该算法包括两个函数knap_ ()1和knap_2().
函数isOverLoad()的作用是判断当前x中记录的搜索路径对应的物品重量是否超过装载上限C,若超载,则返回本次递归调用,相当于剪枝。参数*price为一指针型变量,是用来返回最大价值量的。返回值被knap_2()函数利用(这也是为什么一个是指针变量一个是不是指针变量)
程序如下:
#include"stdio.h"
int x[100];
int val[100]={-1}; //存放物品单价
int weight[100]={-1}; //存放物品重量
//限重函数
int isOverLoad(int n,int c)
{
int i,w=0;
for(i=0;i<n;i++)
w=w+weight[i]*x[i];
if(w>c) return 1;
else return 0;
}
//求总价值函数
int getval(n)
{
int i,v=0;
for(i=0;i<n;i++)
v=v+x[i]*vai[i];
return v;
}
//求最高价值函数
knap_1(int n,int flag,int c,int *price) //n为x数组下标,flag为物品数量
{
int i,j,p;
if(isOverLoad(n,c)) return; //剪枝
if(n==flag)
{
p=getval(n);
if(*price<p) *price=p;
return;
}
for(i=0;i<n;i++)
{
x[n]=i;
knap_1(n+1,flag,c,price);
}
}
//找最佳方案函数
knap_2(int n,int flag,int c,int price)
{
int i,j,p;
if(isOverLoad(n,c)) return;
if(n==flag)
{
p=getval(n);
if(price==p)
{
printf("---------bag----------\n");
for(j=0;j<n;j++)
if(x[j]==1)
{
printf("| |p%d: | |\n",j);
printf("| |weight:%2d kg| |\n",weight[j]);
printf("| |price:%2d $ | |\n",val[j]);
}
printf("--------------------\n");
getche();
return;
}
for(i=0;i<n;i++)
{
x[n]=i;
knap_2(n+1,flag,c,price);
}
}
main()
{
int price=0,i,n,c;
printf("Input the number of products:\n");
scanf("%d",&n);
printf("Input the weight of each product\n");
for(i=0;i<n;i++)
scanf("%d",&weight[i]);
printf("Input the limit weight the bag can overload:\n");
scanf("%d",&c);
printf("Input the price of each product\n");
for(i=0;i<n;i++)
scanf("%d",&val[i]);
knap_1(0,n,c,&price);
knap_2(0,n,c,price);
printf("The grass price :%d $",price);
getche();
}