解01背包问题有很多种方法,就我知道的就有动态规划,回溯法,分支界限法这几种,下面就列出我的回溯法解法,以供参考
class Knap
{
public:
Knap(double *pp,double *ww,double cc,int nn)//构造函数
{
p=pp;
w=ww;
c=cc;
n=nn;
cw=0;
cp=0;
bestp=0;
x=new int[n+1];
bestx=new int[n+1];
}
double knapsack();//找最优值的函数。
double Bound(int i);//边界函数
void BackTrack(int i);//回溯函数
void output()//输出最佳路径
{
for(int i=1;i<=n;i++)
cout<<bestx[i]<<" ";
cout<<endl;
}
private:
double c;
int n;
double *w;
double *p;
double cw;
double cp;
double bestp;
int *x;
int *bestx;
};
void Knap::BackTrack(int i)
{
if(i>n)
{
if(cp>bestp)/*保证最优,这样下面的bestx就是最优的路径。*/
{
bestp=cp;
for(int i=1;i<=n;i++)
bestx[i]=x[i];
}
return;
}
if(cw+w[i]<=c)//left
{
x[i]=1;//记录进入坐子树
cw+=w[i];
cp+=p[i];
BackTrack(i+1);
cw-=w[i];
cp-=p[i];
}
if(Bound(i+1)>bestp)//right
{
x[i]=0;//记录进入右子树
BackTrack(i+1);
}
}
class Object
{
public:
int ID;
double d;
};
int cmp(Object a,Object b)//定于排序类型。
{
return a.d>b.d;//降序
}
double Knap::Bound(int i)
{
Object *Q=new Object[n+1];
int j;
for(j=1;j<=n;j++)
{
Q[j].ID=j;
Q[j].d=1.0*p[j]/w[j];
}
sort(Q+1,Q+n+1,cmp);/*对数组Q排序。按cmp()方式排序。这里要注意是从Q[1]开始而不是从Q开始*/
/*
for(j=1;j<=n;j++)
cout<<Q[j].ID<<" ";
cout<<endl;
*/
double cleft=c-cw;
double b=cp;
while(i<=n&&w[Q[i].ID]<=cleft)
{
cleft-=w[Q[i].ID];
b+=p[Q[i].ID];
i++;
}
if(i<=n)
b+=1.0*p[Q[i].ID]*cleft/w[Q[i].ID];/*如果不能完整装入一个物品,则可以装入部分。*/
return b;
}
double Knap::knapsack()
{
/*分支判断,如果背包容量足够大,就不用再回溯搜索了。*/
int i;
double W=0;
double P=0;
for(i=1;i<=n;i++)
{
W+=w[i];
P+=p[i];
}
if(W<=c)
return P;
/*如果背包容量不够大,则有最有的方案,使用回溯方法。*/
BackTrack(1);
return bestp;
}
int main()
{
int n=4;
double c=7;//背包容量
/*注意点,这里的数组p[]和w[]的第一个元素是-1,这是因为我在操作过程中都是从数组元素的1开始的,而我们知道数组中第一个元素是0号元素,所以我这里用-1填上*/
double p[]={-1,9,10,7,4};//物品价值
double w[]={-1,3,5,2,1};//物品重量
Knap k=Knap(p,w,c,n);
cout<<k.knapsack()<<endl;
k.output();
cout<<k.Bound(1)<<endl;//输出为22,是最大理想值
return 1;
}