算法 01背包问题.docx
《算法 01背包问题.docx》由会员分享,可在线阅读,更多相关《算法 01背包问题.docx(18页珍藏版)》请在冰豆网上搜索。
算法01背包问题
一、实验目的与要求
掌握回溯法、分支限界法的原理,并能够按其原理编程实现解决0-1背包问题,以加深对回溯法、分支限界法的理解。
1.要求分别用回溯法和分支限界法求解0-1背包问题;
2.要求交互输入背包容量,物品重量数组,物品价值数组;
3.要求显示结果。
二、实验方案
在选择装入背包的物品时,对每种物品i只有2种选择,即装入背包或不装入背包。
不能将物品i装入背包多次,也不能只装入部分的物品i。
三、实验结果和数据处理
1.用回溯法解决0-1背包问题:
代码:
importjava.util.*;
publicclassKnapsack
{
privatedouble[]p,w;//分别代表价值和重量
privateintn;
privatedoublec,bestp,cp,cw;
privateintx[];//记录可选的物品
privateint[]cx;
publicKnapsack(doublepp[],doubleww[],doublecc)
{
this.p=pp;this.w=ww;this.n=pp.length-1;
this.c=cc;this.cp=0;this.cw=0;
this.bestp=0;
x=newint[ww.length];
cx=newint[pp.length];
}
voidKnapsack()
{
backtrack(0);
}
voidbacktrack(inti)
{
if(i>n)
{//判断是否到达了叶子节点
if(cp>bestp)
{
for(intj=0;jx[j]=cx[j];
bestp=cp;
}
return;
}
if(cw+w[i]<=c)
{//搜索右子树
cx[i]=1;
cw+=w[i];
cp+=p[i];
backtrack(i+1);
cw-=w[i];
cp-=p[i];
}
cx[i]=0;
backtrack(i+1);//检查左子树
}
voidprintResult()
{
System.out.println("回溯法");
System.out.println("物品个数:
n=4");
System.out.println("背包容量:
c=7");
System.out.println("物品重量数组:
w={3,5,2,1}");
System.out.println("物品价值数组:
p={9,10,7,4}");
System.out.println("最优值:
="+bestp);
System.out.println("选中的物品是:
");
for(inti=0;i{
System.out.print(x[i]+"");
}
}
publicstaticvoidmain(String[]args)
{
doublep[]={9,10,7,4};
doublew[]={3,5,2,1};
intmaxweight=7;
Knapsackks=newKnapsack(p,w,maxweight);
ks.Knapsack();//回溯搜索
ks.printResult();
}
}
运行结果:
2.用优先队列式分支限界法解决0-1背包问题:
代码:
publicclassKnapsack
{
staticdoublec;
staticintn;
staticdoublew[];
staticdoublep[];
staticdoublecw;
staticdoublecp;
staticintbestX[];
staticMaxHeapheap;
//上界函数bound计算结点所相应价值的上界
privatestaticdoublebound(inti)
{
doublecleft=c-cw;
doubleb=cp;
while(i<=n&&w[i]<=cleft)
{
cleft=cleft-w[i];
b=b+p[i];
i++;
}
//装填剩余容量装满背包
if(i<=n)
b=b+p[i]/w[i]*cleft;
returnb;
}
//addLiveNode将一个新的活结点插入到子集树和优先队列中
privatestaticvoidaddLiveNode(doubleup,doublepp,doubleww,intlev,BBnodepar,booleanch)
{
//将一个新的活结点插入到子集树和最大堆中
BBnodeb=newBBnode(par,ch);
HeapNodenode=newHeapNode(b,up,pp,ww,lev);
heap.put(node);
}
privatestaticdoubleMaxKnapsack()
{
//优先队列式分支限界法,返回最大价值,bestx返回最优解
BBnodeenode=null;
inti=1;
doublebestp=0;//当前最优值
doubleup=bound
(1);//当前上界
while(i!
=n+1){//非叶子结点
//检查当前扩展结点的左儿子子结点
doublewt=cw+w[i];
if(wt<=c){
if(cp+p[i]>bestp)
bestp=cp+p[i];
addLiveNode(up,cp+p[i],cw+w[i],i+1,enode,true);
}
up=bound(i+1);
if(up>=bestp)
addLiveNode(up,cp,cw,i+1,enode,false);
HeapNodenode=(HeapNode)heap.removeMax();
enode=node.liveNode;
cw=node.weight;
cp=node.profit;
up=node.upperProfit;
i=node.level;
}
for(intj=n;j>0;j--)
{
bestX[j]=(enode.leftChild)?
1:
0;
enode=enode.parent;
}
returncp;
}
publicstaticdoubleKnapsack(doublepp[],doubleww[],doublecc,intxx[])
{
//返回最大值,bestX返回最优解
c=cc;
n=pp.length-1;
//定义以单位重量价值排序的物品数组
Elementq[]=newElement[n];
doublews=0.0;
doubleps=0.0;
for(inti=0;i{
q[i]=newElement(i+1,pp[i+1]/ww[i+1]);
ps=ps+pp[i+1];
ws=ws+ww[i+1];
}
if(ws<=c)
{
returnps;
}
p=newdouble[n+1];
w=newdouble[n+1];
for(inti=0;i{
p[i+1]=pp[q[i].id];
w[i+1]=ww[q[i].id];
}
cw=0.0;
cp=0.0;
bestX=newint[n+1];
heap=newMaxHeap(n);
doublebestp=MaxKnapsack();
for(intj=0;jxx[q[j].id]=bestX[j+1];
returnbestp;
}
publicstaticvoidmain(String[]args)
{
doublew[]=newdouble[5];
w[1]=3;w[2]=5;w[3]=2;w[4]=1;
doublep[]=newdouble[5];
p[1]=9;p[2]=10;p[3]=7;p[4]=4;
doublec=7;
intx[]=newint[5];
doublem=Knapsack(p,w,c,x);
System.out.println("优先队列式分支限界法:
");
System.out.println("物品个数:
n=4");
System.out.println("背包容量:
c=7");
System.out.println("物品重量数组:
w={3,5,2,1}");
System.out.println("物品价值数组:
p={9,10,7,4}");
System.out.println("最优值:
="+m);
System.out.println("选中的物品是:
");
for(inti=1;i<=4;i++)
System.out.print(x[i]+"");
}
}
//子空间中节点类型
classBBnode
{
BBnodeparent;//父节点
booleanleftChild;//左儿子节点标志
BBnode(BBnodepar,booleanch)
{
parent=par;
leftChild=ch;
}
}
classHeapNodeimplementsComparable
{
BBnodeliveNode;//活结点
doubleupperProfit;//结点的价值上界
doubleprofit;//结点所相应的价值
doubleweight;//结点所相应的重量
intlevel;//活结点在子集树中所处的层次号
//构造方法
publicHeapNode(BBnodenode,doubleup,doublepp,doubleww,intlev)
{
liveNode=node;
upperProfit=up;
profit=pp;
weight=ww;
level=lev;
}
publicintcompareTo(Objecto)
{
doublexup=((HeapNode)o).upperProfit;
if(upperProfitreturn-1;
if(upperProfit==xup)
return0;
else
return1;
}
}
classElementimplementsComparable
{
intid;
doubled;
publicElement(intidd,doubledd)
{
id=idd;
d=dd;
}
publicintcompareTo(Objectx)
{
doublexd=((Element)x).d;
if(dif(d==xd)return0;
return1;
}
publicbooleanequals(Objectx)
{
returnd==((Element)x).d;
}
}
classMaxHeap
{
staticHeapNode[]nodes;
staticintnextPlace;
staticintmaxNumber;
publicMaxHeap(intn)
{
maxNumber=(int)Math.pow((double)2,(double)n);
nextPlace=1;//下一个存放位置
nodes=newHeapNode[maxNumber];
}
publicstaticvoidput(HeapNodenode)
{
nodes[nextPlace]=node;
nextPlace++;
heapSort(nodes);
}
publicstaticHeapNoderemoveMax()
{
HeapNodetempNode=nodes[1];
nextPlace--;
nodes[1]=nodes[nextPlace];
heapSort(nodes);
returntempNode;
}
privatestaticvoidheapAdjust(HeapNode[]nodes,ints,intm)
{
HeapNoderc=nodes[s];
for(intj=2*s;j<=m;j*=2)
{
if(j++j;
if(!
(rc.upperProfitbreak;
nodes[s]=nodes[j];
s=j;
}
nodes[s]=rc;
}
privatestaticvoidheapSort(HeapNode[]nodes)
{
for(inti=(nextPlace-1)/2;i>0;--i)
{
heapAdjust(nodes,i,nextPlace-1);
}
}
}
运行结果:
3.用队列式分支限界法解决0-1背包问题:
代码:
#include
#include
#defineMAXNUM100
structnode
{
intstep;
doubleprice;
doubleweight;
doublemax,min;
unsignedlongpo;
};
typedefstructnodeDataType;
structSeqQueue
{/*顺序队列类型定义*/
intf,r;
DataTypeq[MAXNUM];
};
typedefstructSeqQueue*PSeqQueue;
PSeqQueuecreateEmptyQueue_seq(void)
{
PSeqQueuepaqu;
paqu=(PSeqQueue)malloc(sizeof(structSeqQueue));
if(paqu==NULL)
printf("Outofspace!
!
\n");
else
paqu->f=paqu->r=0;
returnpaqu;
}
intisEmptyQueue_seq(PSeqQueuepaqu)
{
returnpaqu->f==paqu->r;
}
/*在队列中插入一元素x*/
voidenQueue_seq(PSeqQueuepaqu,DataTypex)
{
if((paqu->r+1)%MAXNUM==paqu->f)
printf("Fullqueue.\n");
else
{
paqu->q[paqu->r]=x;
paqu->r=(paqu->r+1)%MAXNUM;
}
}
/*删除队列头元素*/
voiddeQueue_seq(PSeqQueuepaqu)
{
if(paqu->f==paqu->r)
printf("EmptyQueue.\n");
else
paqu->f=(paqu->f+1)%MAXNUM;
}
/*对非空队列,求队列头部元素*/
DataTypefrontQueue_seq(PSeqQueuepaqu)
{
return(paqu->q[paqu->f]);
}
/*物品按性价比从新排序*/
voidsort(intn,doublep[],doublew[])
{
inti,j;
for(i=0;ifor(j=i;j{
doublea=p[j]/w[j];
doubleb=p[j+1]/w[j+1];
if(a
{
doubletemp=p[j];
p[j]=p[j+1];
p[j+1]=temp;
temp=w[j];
w[j]=w[j+1];
w[j+1]=temp;
}
}
}
/*求最大可能值*/
doubleup(intk,doublem,intn,doublep[],doublew[])
{
inti=k;
doubles=0;
while(i{
m-=w[i];
s+=p[i];
i++;
}
if(i0)
{
s+=p[i]*m/w[i];
i++;
}
returns;
}
/*求最小可能值*/
doubledown(intk,doublem,intn,doublep[],doublew[])
{
inti=k;
doubles=0;
while(i{
m-=w[i];
s+=p[i];
i++;
}
returns;
}
/*用队列实现分支定界算法*/
doublesolve(doublem,intn,doublep[],doublew[],unsignedlong*po)
{
doublemin;
PSeqQueueq=createEmptyQueue_seq();
DataTypex={0,0,0,0,0,0};
sort(n,p,w);
x.max=up(0,m,n,p,w);
x.min=min=down(0,m,n,p,w);
if(min==0)return-1;
enQueue_seq(q,x);
while(!
isEmptyQueue_seq(q))
{
intstep;
DataTypey;
x=frontQueue_seq(q);
deQueue_seq(q);
if(x.maxstep=x.step+1;
if(step==n+1)continue;
y.max=x.price+up(step,m-x.weight,n,p,w);
if(y.max>=min)
{
y.min=x.price+down(step,m-x.weight,n,p,w);
y.price=x.price;
y.weight=x.weight;
y.step=step;
y.po=x.po<<1;
if(y.min>=min)
{
min=y.min;
if(step==n)*po=y.po;
}
enQueue_seq(q,y);
}
if(x.weight+w[step-1]<=m)
{
y.max=x.price+p[step-1]+
up(step,m-x.weight-w[step-1],n,p,w);
if(y.max>=min){
y.min=x.price+p[step-1]+
down(step,m-x.weight-w[step-1],n,p,w);
y.price=x.price+p[step-1];
y.weight=x.weight+w[step-1];
y.step=step;
y.po=(x.po<<1)+1;
if(y.min>=min)
{
min=y.min;
if(step==n)*po=y.po;
}
enQueue_seq(q,y);
}
}
}
returnmin;
}
#definen4
doublem=7;
doublep[n]={9,10,7,4};
doublew[n]={3,5,1,2};
intmain()
{
inti;
doubled;
unsignedlongpo;
d=solve(m,n,p,w,&po);
if(d==-1)
printf("Nosolution!
\n");
else
{
for(i=0;iprintf("x%d为%d\n",i+1,((po&(1<<(n-i-1)))!
=0));
printf("最优值是:
%f\n",d);
}
getchar();
return0;
}
运行结果:
(学习的目的是增长知识,提高能力,相信一分耕耘一分收获,努力就一定可以获得应有的回报)