题目
给定n个重量为w1w1,w2w2,w3w3,…,wnwn,价值为v1v1,v2v2,v3v3,…,vnvn的物品和容量为CC的背包,求这个物品中一个最有价值的子集,使得在满足背包的容量的前提下,包内的总价值最大。
分析
这个题目可以使用回溯法,回溯法即是当我们得到一个序列时回到上一步的状态,比如有n个物品,我们可以抽象成一棵满二叉树,向左表示将物品装入包中,向右表示不装入包中,当n为3时我们可以得到一个这样的满二叉树。
frist.png
可以看到我们构建了一个满二叉树只要便利这个二叉树找到符合条件的就可以了。
代码
#include <stdio.h>
#include <stdlib.h>
int n=8; //最大的物品个数
int V=110;
int max=0;
int v[]={11,21,31,33,43,53,55,65};
int w[]={1,11,21,23,33,43,45,55};
int sumV=0; //总价值
int sumW=0; //总体积
int digui(int m)
{
if(sumW+w[m]>V || m>=n)
{
if(max<sumV)
max=sumV;
//printf("%d\n",max);
return 0;
}
sumW+=w[m];
sumV+=v[m];
digui(m+1);
sumW-=w[m];
sumV-=v[m];
digui(m+1);
}
int main()
{
/*printf(“输入物品的件数和总体积:\n”);
scanf("%d%d",&n,&V);
int i;
for(i=0;i<n;++i)
{
printf(“输入第%d件物品的价值:\n”,i+1);
scanf("%d",&w[i]);
printf(“输入第%d件物品的体积:\n”,i+1);
scanf("%d",&v[i]);
}
*/
digui(0);
printf("%d",max);
return 0;
}
代码分析
我们使用了递归的方法,判断是否越界和是否超过最大的体积,然我们看一下这段代码。
sumW+=w[m];
sumV+=v[m];
digui(m+1);
sumW-=w[m];
sumV-=v[m];
digui(m+1);
可以看出我们首先sumW+=w[m];sumV+=v[m]; digui(m+1);这三句表示将第m+1个物品放入包中,然后递归下一个物品看是否符合条件,sumW-=w[m];sumV-=v[m];
digui(m+1);这三句便是表示回溯的,这个digui(m+1)表示没有将第m+1个物品放入包中(注意这里的m+i个物品并不是digui(m+1)里面的m+1,而是我们的下标是从0开始的。)。
eg:我们可以举一个例子来进行详细说明一下。
如果现在有四个物品A,B,C,D.执行 sumW+=w[m]; sumV+=v[m];digui(m+1);后便会将ABC三个物品都放入包中(假设将D放入其中的话包的空间不够),然后便会执行sumW-=w[m];sumV-=v[m];digui(m+1);,前两句表示将下标为m的物品从包中取出,因此此时这个物品是没有装入包的,然后执行digui(m+1);,表示递归,一个循环的操作。。。。