回溯法和分支限界法解决01背包题精教学文案.docx
《回溯法和分支限界法解决01背包题精教学文案.docx》由会员分享,可在线阅读,更多相关《回溯法和分支限界法解决01背包题精教学文案.docx(20页珍藏版)》请在冰点文库上搜索。
回溯法和分支限界法解决01背包题精教学文案
0-1背包问题
计科1班朱润华2012040732
方法1:
回溯法
一、回溯法描述:
用回溯法解问题时,应明确定义问题的解空间。
问题的解空间至少包含问题的一个(最优)解。
对于0-1背包问题,解空间由长度为n的0-1向量组成。
该解空间包含对变量的所有0-1赋值。
例如n=3时,解空间为:
{(0,0,0),(0,1,0),(0,0,1),(1,0,0),(0,1,1),(1,0,1),(1,1,0),(1,1,1)}然后可将解空间组织成树或图的形式,0-1背包则可用完全二叉树表示其解空间给定n种物品和一背包。
物品i的重量是wi,其价值为vi,背包的容量为C。
问:
应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
形式化描述:
给定c>0,wi>0,vi>0,1≤i≤n.要求找一n元向量(x1,x2,…,xn,),xi∈{0,1},?
∑wixi≤c,且∑vixi达最大.即一个特殊的整数规划问题。
二、回溯法步骤思想描述:
0-1背包问题是子集选取问题。
0-1背包问题的解空间可以用子集树表示。
在搜索解空间树时,只要其左儿子节点是一个可行节点,搜索就进入左子树。
当右子树中有可能含有最优解时,才进入右子树搜索。
否则,将右子树剪去。
设r是当前剩余物品价值总和,cp是当前价值;bestp是当前最优价值。
当cp+r<=bestp时,可剪去右子树。
计算右子树上界的更好的方法是将剩余物品依次按其单位价值排序,然后依次装入物品,直至装不下时,再装入物品一部分而装满背包。
例如:
对于0-1背包问题的一个实例,n=4,c=7,p=[9,10,7,4],w=[3,5,2,1]。
这4个物品的单位重量价值分别为[3,2,3,5,4]。
以物品单位重量价值的递减序装入物品。
先装入物品4,然后装入物品3和1.装入这3个物品后,剩余的背包容量为1,只能装0.2的物品2。
由此得一个解为[1,0.2,1,1],其相应价值为22。
尽管这不是一个可行解,但可以证明其价值是最优值的上界。
因此,对于这个实例,最优值不超过22。
在实现时,由Bound计算当前节点处的上界。
类Knap的数据成员记录解空间树中的节点信息,以减少参数传递调用所需要的栈空间。
在解空间树的当前扩展节点处,仅要进入右子树时才计算上界Bound,以判断是否可将右子树剪去。
进入左子树时不需要计算上界,因为上界预期父节点的上界相同。
三、回溯法实现代码:
#include"stdafx.h"
#include
usingnamespacestd;
template
classKnap
{
template
friendTypepKnapsack(Typep[],Typew[],Typew,int);
private:
TypepBound(inti);
voidBacktrack(inti);
Typewc;//背包容量
intn;//物品数
Typew*w;//物品重量数组
Typep*p;//物品价值数组
Typewcw;//当前重量
Typepcp;//当前价值
Typepbestp;//当前最后价值
};
template
TypepKnapsack(Typepp[],Typeww[],Typewc,intn);
template
inlinevoidSwap(Type&a,Type&b);
template
voidBubbleSort(Typea[],intn);
intmain()
{
intn=4;//物品数
intc=7;//背包容量
intp[]={0,9,10,7,4};//物品价值下标从1开始
intw[]={0,3,5,2,1};//物品重量下标从1开始
cout<<"背包容量为:
"<cout<<"物品重量和价值分别为:
"<for(inti=1;i<=n;i++)
{
cout<<"("<}
cout<cout<<"背包能装下的最大价值为:
"<}
template
voidKnap:
:
Backtrack(inti)
{
if(i>n)//到达叶子节点
{
bestp=cp;
return;
}
if(cw+w[i]<=c)//进入左子树
{
cw+=w[i];
cp+=p[i];
Backtrack(i+1);
cw-=w[i];
cp-=p[i];
}
if(Bound(i+1)>bestp)//进入右子树
{
Backtrack(i+1);
}
}
template
TypepKnap:
:
Bound(inti)//计算上界{
Typewcleft=c-cw;//剩余容量
Typepb=cp;
//以物品单位重量价值递减序装入物品
while(i<=n&&w[i]<=cleft)
{
cleft-=w[i];
b+=p[i];
i++;
}
//装满背包
if(i<=n)
{
b+=p[i]/w[i]*cleft;
}
returnb;
}
classObject
{
template
friendTypepKnapsack(Typep[],Typew[],Typew,int);public:
intoperator<=(Objecta)const
{
return(d>=a.d);
}
private:
intID;
floatd;
};
template
TypepKnapsack(Typepp[],Typeww[],Typewc,intn){
//为Knap:
:
Backtrack初始化
TypewW=0;
TypepP=0;
Object*Q=newObject[n];
for(inti=1;i<=n;i++)
{
Q[i-1].ID=i;
Q[i-1].d=1.0*p[i]/w[i];
P+=p[i];
W+=w[i];
}
if(W<=c)//装入所有物品
{
returnP;
}
//依物品单位重量价值排序
BubbleSort(Q,n);
KnapK;
K.p=newTypep[n+1];
K.w=newTypew[n+1];
for(inti=1;i<=n;i++)
{
K.p[i]=p[Q[i-1].ID];
K.w[i]=w[Q[i-1].ID];
}
K.cp=0;
K.cw=0;
K.c=c;
K.n=n;
K.bestp=0;
//回溯搜索
K.Backtrack
(1);
delete[]Q;
delete[]K.w;
delete[]K.p;
returnK.bestp;
}
template
voidBubbleSort(Typea[],intn)
{
//记录一次遍历中是否有元素的交换
boolexchange;
for(inti=0;i{
exchange=false;
for(intj=i+1;j<=n-1;j++)
{
if(a[j]<=a[j-1])
{
Swap(a[j],a[j-1]);
exchange=true;
}
}
//如果这次遍历没有元素的交换,那么排序结束if(false==exchange)
{
break;
}
}
}
template
inlinevoidSwap(Type&a,Type&b)
{
Typetemp=a;
a=b;
b=temp;
}
四、程序运行结果:
五、回溯法解决0-1背包问题复杂度分析:
计算上界需要O(n)时间,在最坏情况下有O(2^n)个右儿子节点需要计算上界,故解0-1背包问题的回溯算法所需要的计算时间为O(n2^n)。
方法2:
分支限界法:
一、分支限界法描述:
给定n种物品和一背包。
物品i的重量是wi,其价值为vi,背包的容量为C。
问:
应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
形式化描述:
给定c>0,wi>0,vi>0,1≤i≤n.要求找一n元向量(x1,x2,…,xn,),xi∈{0,1},∋∑wixi≤c,且∑vixi达最大.即一个特殊的整数规划问题。
二、分支限界法步骤思想:
首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。
在优先队列分支限界法中,节点的优先级由已装袋的物品价值加上剩下的最大单位重量价值的物品装满剩余容量的价值和。
算法首先检查当前扩展结点的左儿子结点的可行性。
如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。
当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。
当扩展到叶节点时为问题的最优值。
例如:
0-1背包问题,当n=3时,w={16,15,15},p={45,25,25},c=30。
优先队列式分支限界法:
处理法则:
价值大者优先。
{}—>{A}—>{B,C}—>{C,D,E}—>{C,E}—>{C,J,K}—>{C}—>{F,G}—>{G,L,M}—>{G,M}—>{G}—>{N,O}—>{O}—>{}。
三、分支限界法解决0-1背包问题实现代码:
//0-1背包问题分支限界法求解
#include"stdafx.h"
#include"MaxHeap.h"
#include
usingnamespacestd;
classObject
{
template
friendTypepKnapsack(Typepp[],Typeww[],Typewc,intn,intbestx[]);public:
intoperator<=(Objecta)const
{
returnd>=a.d;
}
private:
intID;
floatd;//单位重量价值
};
templateclassKnap;
classbbnode
{
friendKnap;
template
friendTypepKnapsack(Typepp[],Typeww[],Typewc,intn,intbestx[]);private:
bbnode*parent;//指向父节点的指针
boolLChild;//左儿子节点标识
};
template
classHeapNode
{
friendKnap;
public:
operatorTypep()const
{
returnuprofit;
}
private:
Typepuprofit,//节点的价值上界
profit;//节点所相应的价值
Typewweight;//节点所相应的重量
intlevel;//活节点在子集树中所处的层序号
bbnode*ptr;//指向活节点在子集中相应节点的指针
};
template
classKnap
{
template
friendTypepKnapsack(Typepp[],Typeww[],Typewc,intn,intbestx[]);public:
TypepMaxKnapsack();
private:
MaxHeap>*H;
TypepBound(inti);
voidAddLiveNode(Typepup,Typepcp,Typewcw,boolch,intlev);
bbnode*E;//指向扩展节点的指针
Typewc;//背包容量
intn;//物品数
Typew*w;//物品重量数组
Typep*p;//物品价值数组
Typewcw;//当前重量
Typepcp;//当前价值
int*bestx;//最优解
};
template
inlinevoidSwap(Type&a,Type&b);
template
voidBubbleSort(Typea[],intn);
intmain()
{
intn=3;//物品数
intc=30;//背包容量
intp[]={0,45,25,25};//物品价值下标从1开始
intw[]={0,16,15,15};//物品重量下标从1开始
intbestx[4];//最优解
cout<<"背包容量为:
"<cout<<"物品重量和价值分别为:
"<for(inti=1;i<=n;i++)
{
cout<<"("<}
cout<cout<<"背包能装下的最大价值为:
"<"<for(inti=1;i<=n;i++)
{
cout<}
cout<return0;
}
template
TypepKnap:
:
Bound(inti)//计算节点所相应价值的上界{
Typewcleft=c-cw;//剩余容量高
Typepb=cp;//价值上界
//以物品单位重量价值递减序装填剩余容量
while(i<=n&&w[i]<=cleft)
{
cleft-=w[i];
b+=p[i];
i++;
}
//装填剩余容量装满背包
if(i<=n)
{
b+=p[i]/w[i]*cleft;
}
returnb;
}
//将一个活节点插入到子集树和优先队列中
template
voidKnap:
:
AddLiveNode(Typepup,Typepcp,Typewcw,boolch,intlev){
bbnode*b=newbbnode;
b->parent=E;
b->LChild=ch;
HeapNodeN;
N.uprofit=up;
N.profit=cp;
N.weight=cw;
N.level=lev;
N.ptr=b;
H->Insert(N);
}
//优先队列式分支限界法,返回最大价值,bestx返回最优解
template
TypepKnap:
:
MaxKnapsack()
{
H=newMaxHeap>(1000);
//为bestx分配存储空间
bestx=newint[n+1];
//初始化
inti=1;
E=0;
cw=cp=0;
Typepbestp=0;//当前最优值
Typepup=Bound
(1);//价值上界
//搜索子集空间树
while(i!
=n+1)
{
//检查当前扩展节点的左儿子节点
Typewwt=cw+w[i];
if(wt<=c)//左儿子节点为可行节点
{
if(cp+p[i]>bestp)
{
bestp=cp+p[i];
}
AddLiveNode(up,cp+p[i],cw+w[i],true,i+1);}
up=Bound(i+1);
//检查当前扩展节点的右儿子节点
if(up>=bestp)//右子树可能含有最优解
{
AddLiveNode(up,cp,cw,false,i+1);
}
//取下一扩展节点
HeapNodeN;
H->DeleteMax(N);
E=N.ptr;
cw=N.weight;
cp=N.profit;
up=N.uprofit;
i=N.level;
}
//构造当前最优解
for(intj=n;j>0;j--)
{
bestx[j]=E->LChild;
E=E->parent;
}
returncp;
}
//返回最大价值,bestx返回最优解
template
TypepKnapsack(Typepp[],Typeww[],Typewc,intn,intbestx[]){
//初始化
TypewW=0;//装包物品重量
TypepP=0;//装包物品价值
//定义依单位重量价值排序的物品数组Object*Q=newObject[n];for(inti=1;i<=n;i++)
{
//单位重量价值数组
Q[i-1].ID=i;
Q[i-1].d=1.0*p[i]/w[i];P+=p[i];
W+=w[i];
}
if(W<=c)
{
returnP;//所有物品装包}
//依单位价值重量排序
BubbleSort(Q,n);
//创建类Knap的数据成员
KnapK;
K.p=newTypep[n+1];
K.w=newTypew[n+1];
for(inti=1;i<=n;i++)
{
K.p[i]=p[Q[i-1].ID];K.w[i]=w[Q[i-1].ID];}
K.cp=0;
K.cw=0;
K.c=c;
K.n=n;
//调用MaxKnapsack求问题的最优解Typepbestp=K.MaxKnapsack();for(intj=1;j<=n;j++)
{
bestx[Q[j-1].ID]=K.bestx[j];}
deleteQ;
delete[]K.w;
delete[]K.p;
delete[]K.bestx;
returnbestp;
}
template
voidBubbleSort(Typea[],intn)
{
//记录一次遍历中是否有元素的交换
boolexchange;
for(inti=0;i{
exchange=false;
for(intj=i+1;j<=n-1;j++)
{
if(a[j]<=a[j-1])
{
Swap(a[j],a[j-1]);
exchange=true;
}
}
//如果这次遍历没有元素的交换,那么排序结束if(false==exchange)
{
break;
}
}
大学生的消费是多种多样,丰富多彩的。
除食品外,很大一部分开支都用于。
服饰,娱乐,小饰品等。
女生都比较偏爱小饰品之类的消费。
女生天性爱美,对小饰品爱不释手,因为饰品所展现的魅力,女人因饰品而妩媚动人,亮丽。
据美国商务部调查资料显示女人占据消费市场最大分额,随社会越发展,物质越丰富,女性的时尚美丽消费也越来越激烈。
因此也为饰品业创造了无限的商机。
据调查统计,有50%的同学曾经购买过DIY饰品,有90%的同学表示若在学校附近开设一家DIY手工艺制品,会去光顾。
我们认为:
我校区的女生就占了80%。
相信开饰品店也是个不错的创业方针。
}
template
inlinevoidSwap(Type&a,Type&b)
{
Typetemp=a;
标题:
大学生究竟难在哪?
—创业要迈五道坎2004年3月23日a=b;
随科技的迅速发展,人们的生活日益趋向便捷、快速,方便,对于我国传统的手工艺制作,也很少有人问津,因此,我组想借此创业机会,在校园内开个DIY创意小屋。
它包括编织、刺绣、串珠等,让我们传统的手工制作也能走进大学,丰富我们的生活。
b=temp;
8、你是如何得志DIY手工艺制品的?
}
众上所述,我们认为:
我们的创意小屋计划或许虽然会有很多的挑战和困难,但我们会吸取和借鉴“漂亮女生”和“碧芝”的成功经验,在产品的质量和创意上多下工夫,使自己的产品能领导潮流,领导时尚。
在它们还没有打入学校这个市场时,我们要巩固我们的学生市场,制作一些吸引学生,又有使学生能接受的价格,勇敢的面对它们的挑战,使自己立于不败之地。
四、程序运行结果:
这里有营业员们向顾客们示范着制作各种风格炯异的饰品,许多顾客也是学得不亦乐乎。
据介绍,经常光顾“碧芝”的都是些希望得到世界上“独一无二”饰品的年轻人,他们在琳琅满目的货架上挑选,然后亲手串连,他们就是偏爱这种DIY的方式,完全自助在现场,有上班族在里面精挑细选成品,有细心的小女孩在仔细盘算着用料和价钱,准备自己制作的原料。
可以想见,用本来稀奇的原料,加上别具匠心的制作,每一款成品都必是独一无二的。
而这也许正是自己制造所能带来最大的快乐吧。
综上所述,DIY手工艺品市场致所以受到认可、欢迎的原因就在于此。
我们认为:
这一市场的消费需求的容量是极大的,具有很大的发展潜力,我们的这一创业项目具有成功的前提。
随着社会经济、文化的飞跃发展,人们正从温饱型步入小康型,崇尚人性和时尚,不断塑造个性和魅力的现代文化价值观念,已成为人们的追求目