分支限界法(算法分析与设计)
2015-10-20 15:42
603 查看
0.概念
分支限界法常以广度优先(队列式(先进先出)分支限界)或以最小耗费(最大效益)优先的方式(优先队列分支限界)搜索问题的解空间树。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。
1.与回溯法的不同
求解目标:回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。
搜索方式的不同:回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。
2.广度优先或以最小耗费优先的方式
广度优先:与层次遍历相似,唯一的不同之处在于不再遍历不可行节点
最小耗费优先与贪心算法的不同:
从字面上的意思理解,最小耗费优先,是以最小耗费优先计算,并不是不将较大耗费的节点作为活节点了,只是在作为活节点的时候将较大耗费的那支给剪了;而贪心算法,是只计算最小耗费的节点,较大耗费的节点就不再作为活节点了。
A算法(启发式搜索)、贪心、分支限界求最优解的区别:
对当前优先搜索方向的判断标准为< 有可能的最优解 >。而最优解可以用一个评估函数来做,即已经有的耗散度加上以后有可能的耗度。A算法就是把两个耗散度加在一起,作为当前状态的搜索搜索方向;但是对以后的耗散度的评估是麻烦的,D(迪杰斯特拉,贪心)算法就是把当前有的路的最短的作为,以后耗散度的评估。分支限界算法就是只以以前的耗散度为评估函数。
3.单源最短路径
A.优先队列式分支界限
4.装载问题
子集树
A.队列式分支限界
主要就是用队列层次遍历,当然过程中会剪掉一些明显的不合条件的分支
B.优先队列式分支限界
注意此处优先级的设置是当前载重量+剩余未选择货物的总重量
队列式分支界限法,是将整个解空间树给搜索了一遍,效率比较低。实验测得:当n=10,c1=200,c2=200,wi随机产生时,队列式分支限界的循环次数为1022次,优先队列分支限界的循环次数是53次。
5.TSP
排列树
A.队列式分支限界
优先级是当前路径总量+未排序节点最小出路总量
6.0-1背包
子集树
A.队列式分支限界
优先级:当前总价值+剩余未选择物品总价值
7.最大团问题
A.优先级队列式分支限界
分支限界法常以广度优先(队列式(先进先出)分支限界)或以最小耗费(最大效益)优先的方式(优先队列分支限界)搜索问题的解空间树。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。
1.与回溯法的不同
求解目标:回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。
搜索方式的不同:回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。
2.广度优先或以最小耗费优先的方式
广度优先:与层次遍历相似,唯一的不同之处在于不再遍历不可行节点
最小耗费优先与贪心算法的不同:
从字面上的意思理解,最小耗费优先,是以最小耗费优先计算,并不是不将较大耗费的节点作为活节点了,只是在作为活节点的时候将较大耗费的那支给剪了;而贪心算法,是只计算最小耗费的节点,较大耗费的节点就不再作为活节点了。
A算法(启发式搜索)、贪心、分支限界求最优解的区别:
对当前优先搜索方向的判断标准为< 有可能的最优解 >。而最优解可以用一个评估函数来做,即已经有的耗散度加上以后有可能的耗度。A算法就是把两个耗散度加在一起,作为当前状态的搜索搜索方向;但是对以后的耗散度的评估是麻烦的,D(迪杰斯特拉,贪心)算法就是把当前有的路的最短的作为,以后耗散度的评估。分支限界算法就是只以以前的耗散度为评估函数。
3.单源最短路径
A.优先队列式分支界限
#include<iostream> #include<vector> #include<climits> #include<queue> using namespace std; vector< vector<int> > v; vector<int> dist; int num; struct Node { int i;//顶点i int len;//起点到顶点i的距离 }; struct cmp { bool operator ()(Node a,Node b) { return a.len>b.len; } }; void shortest(int start) { priority_queue<Node,vector<Node>,cmp> q; Node startv; startv.i=start; startv.len=0; q.push(startv); dist[start]=0; while(true) { for(int j=0;j<num;j++)//到哪个节点距离小,就将哪个节点作为扩展节点,并不是要按部就班的深度搜索,也不是以后不将耗费较大的做活节点了 { if(q.top().len+v[q.top().i][j]<dist[j]&&v[q.top().i][j]!=-1) { dist[j]=q.top().len+v[q.top().i][j]; Node temp; temp.i=j; temp.len=dist[j]; q.push(temp); } } if(q.empty()) { break; } else { q.pop(); } } } int main() { cin>>num; for(int i=0;i<num;i++) { vector<int> temp1; for(int j=0;j<num;j++) { int temp2; cin>>temp2;//-1表示没有边,正数表示存在边 temp1.push_back(temp2); } v.push_back(temp1); dist.push_back(INT_MAX); } shortest(0); cout<<dist[num-1]<<endl; }
4.装载问题
子集树
A.队列式分支限界
主要就是用队列层次遍历,当然过程中会剪掉一些明显的不合条件的分支
#include<iostream> #include<vector> #include<queue> #include<cstdlib> using namespace std; struct Node { int i; int w;//到此为止的总重量 }; vector<int> w; int c1; int c2; int num; int sumw; int bestcost; int random(int s,int e) { return s+rand()%(e-s); } void loading() { if(sumw<c1) { bestcost=sumw; return; } queue<Node> q; Node temp; temp.i=0; temp.w=0; q.push(temp);//无效根节点 while(!q.empty()&&q.front().i<num) { Node t=q.front(); q.pop(); for(int i=0;i<=1;i++) { int tempw=t.w+i*w[t.i+1]; if(tempw<=c1) { temp.i=t.i+1; temp.w=tempw; q.push(temp); if(sumw-tempw<=c2&&tempw>bestcost) { bestcost=tempw; } } } } } int main() { bestcost=-1; sumw=0; cin>>num>>c1>>c2; for(int i=0;i<=num;i++)//0作为无效根节点 { int temp=random(1,50); sumw+=temp; w.push_back(temp); cout<<temp<<" "; } cout<<endl; w[0]=0; loading(); if(bestcost!=-1) cout<<"c1: "<<bestcost<<"\nc2: "<<sumw-bestcost<<endl; else cout<<"can't loading"<<endl; return 0; }
B.优先队列式分支限界
注意此处优先级的设置是当前载重量+剩余未选择货物的总重量
#include<iostream> #include<vector> #include<queue> #include<cstdlib> using namespace std; struct Node { int i; int w;//到此为止的总重量 int r;//权重,权重等于w+r[i],剩余货物i+1~num的总重量,因为剩余货物i+1~num有可能全选择,所以考虑权重时要把它也带上 }; struct cmp { bool operator()(Node a,Node b) { return a.r<b.r;//大值优先 } }; vector<int> w; int c1; int c2; int num; int sumw; int bestcost; int random(int s,int e) { return s+rand()%(e-s); } void loading() { if(sumw<=c1) { bestcost=sumw; return; } vector<int> r; for(int i=0;i<=num;i++) { r.push_back(0); } for(int i=num-1;i>0;i--)//剩余货物总重量 { r[i]=r[i+1]+w[i+1]; } priority_queue<Node,vector<Node>,cmp> q; Node temp; temp.i=0; temp.w=0; temp.r=0; q.push(temp); int countN=0; while(!q.empty()&&q.top().i<num)//大顶堆,一直取最大的(不一定是一个分支上),所以只要到达叶子节点就表示已经得最优解了 { countN++; Node t=q.top(); q.pop(); for(int i=0;i<=1;i++) { temp.w=t.w+i*w[t.i+1]; temp.i=t.i+1; temp.r=temp.w+r[temp.i]; if(temp.w<=c1) { q.push(temp); } } } cout<<countN<<endl; bestcost=q.top().w; } int main() { bestcost=-1; sumw=0; cin>>num>>c1>>c2; for(int i=0;i<=num;i++)//0作为无效根节点 { int temp=random(1,50); sumw+=temp; w.push_back(temp); cout<<temp<<" "; } cout<<endl; w[0]=0; loading(); if(bestcost!=-1) cout<<"c1: "<<bestcost<<"\nc2: "<<sumw-bestcost<<endl; else cout<<"can't loading"<<endl; return 0; }C.比较
队列式分支界限法,是将整个解空间树给搜索了一遍,效率比较低。实验测得:当n=10,c1=200,c2=200,wi随机产生时,队列式分支限界的循环次数为1022次,优先队列分支限界的循环次数是53次。
5.TSP
排列树
A.队列式分支限界
#include<iostream> #include<queue> #include<vector> #include<cstdlib> #include<climits> using namespace std; vector< vector<int> > v; vector<bool> x; int num; int bestcost; struct Node { int i; int cost; vector<bool> x; }; int random(int s,int e) { return s+rand()%(e-s); } void tsp(int start) { queue<Node> q; Node t; t.i=start; t.x=x; t.cost=0; t.x[start]=true; q.push(t); while(!q.empty()) { Node te=q.front(); q.pop(); int flag=0; for(int i=0;i<num;i++) { if(!te.x[i]) { t.i=i; t.x=te.x; t.cost=te.cost+v[te.i][t.i]; t.x[i]=true; q.push(t); flag=1; } } if(!flag)//叶子节点了 { if(te.cost+v[te.i][start]<bestcost) { bestcost=te.cost+v[te.i][start]; } } } } int main() { bestcost=INT_MAX; cin>>num; for(int i=0;i<num;i++) { vector<int> temp; for(int j=0;j<num;j++) { temp.push_back(0); } v.push_back(temp); x.push_back(false); } for(int i=0;i<num;i++) { for(int j=i+1;j<num;j++) { int temp=random(1,50); v[i][j]=temp; v[j][i]=temp; } } tsp(0); cout<<bestcost<<endl; }B.优先队列分支限界
优先级是当前路径总量+未排序节点最小出路总量
#include"tsp.h" using namespace std; struct Node { int i;//第i层 int ncost;//当前花费 int rcost;//剩余节点的最小出边和 int lcost;//优先级,为当前花费和剩余节点的最小出边和之和 vector<int> x; }; struct cmp { bool operator()(Node a,Node b) { return a.lcost>b.lcost; } }; void branchboundPQ(const vector< vector<int> > &v,vector<int> &x,int &costbest,int firstcity) { swap(x[firstcity],x[0]); int minsum=0;//最小出边和 vector<int> minout;//每个顶点的最小出边 int num=x.size(); for(int i=0;i<num;i++) { int min=INT_MAX; for(int j=0;j<num;j++) { if(v[x[i]][x[j]]>0&&v[x[i]][x[j]]<min) { min=v[x[i]][x[j]]; } } minout.push_back(min); minsum+=min; } priority_queue<Node,vector<Node>,cmp> p; Node t; t.i=0; t.ncost=0; t.rcost=minsum; t.lcost=0; t.x=x; p.push(t); while(!p.empty()&&p.top().i<num-1) { Node te=p.top(); p.pop(); if(te.i==num-2) { int costT=te.ncost+v[te.x[num-2]][te.x[num-1]]+v[te.x[num-1]][te.x[0]]; if(costT<costbest) { costbest=costT; te.ncost=costbest; te.lcost=costbest; te.i++; p.push(te); } } else { for(int i=te.i+1;i<num;i++) { t.i=te.i+1; t.ncost=te.ncost+v[te.x[te.i]][te.x[i]]; t.rcost=te.rcost-minout[x[te.i]]; t.lcost=t.ncost+t.rcost; t.x=te.x; t.x[te.i+1]=te.x[i]; t.x[i]=te.x[te.i+1]; if(t.lcost<costbest)//当前花费和剩余节点的最小出边和之和小于最小花费,说明可能存在更优解 { p.push(t); } } } } }
6.0-1背包
子集树
A.队列式分支限界
#include<iostream> #include<vector> #include<cstdlib> #include<queue> using namespace std; struct Node { int i; int w;//到i为止的总重量 int v;//到i为止的总价值 }; int num,c,maxv; vector<int> w; vector<int> v; int random(int s,int e) { return s+rand()%(e-s); } void knapsack() { Node t; t.i=-1; t.w=0; t.v=0; queue<Node> q; q.push(t); while(!q.empty()) { Node te=q.front(); q.pop(); for(int i=0;i<=1;i++) { int tempw=te.w+i*w[te.i+1]; int tempv=te.v+i*v[te.i+1]; if(tempw<=c) { t.i=te.i+1; t.w=tempw; t.v=tempv; if(t.i==num-1&&t.v>maxv) { maxv=tempv; } else if(t.i<num-1) { q.push(t); } } } } } int main() { cin>>num>>c; maxv=-1; for(int i=0;i<num;i++) { int temp=random(1,20); w.push_back(temp); cout<<temp<<"("; temp=random(1,50); v.push_back(temp); cout<<temp<<") "; } cout<<endl; knapsack(); cout<<maxv<<endl; return 0; }B.优先队列式分支限界
优先级:当前总价值+剩余未选择物品总价值
#include<iostream> #include<vector> #include<queue> #include<cstdlib> using namespace std; struct Node { int w;//当前重量 int v;//当前总价值 int i;//层号 int p;//优先级:当前价值+剩余未抉择物品价值 }; struct cmp { bool operator()(Node a,Node b) { return a.p<b.p; } }; vector<int> w; vector<int> v; int num,c,maxv,sumv; int random(int s,int e) { return s+rand()%(e-s); } void knapsack() { vector<int> r; priority_queue<Node,vector<Node>,cmp> q; for(int i=0;i<num;i++) { sumv=sumv-v[i]; r.push_back(sumv); } Node t; t.i=-1; t.w=0; t.v=0; t.p=0; q.push(t); while(!q.empty()&&q.top().i<num-1) { Node te=q.top(); q.pop(); for(int i=0;i<=1;i++) { int tempw=te.w+i*w[te.i+1]; int tempv=te.v+i*v[te.i+1]; int tempp=tempv+r[te.i+1]; if(tempw<=c) { t.w=tempw; t.v=tempv; t.p=tempp; t.i=te.i+1; if(t.p>maxv) { q.push(t); } } } } maxv=q.top().v; } int main() { maxv=-1; sumv=0; cin>>num>>c; for(int i=0;i<num;i++) { int temp=random(1,20); w.push_back(temp); cout<<temp<<"<"; temp=random(1,50); v.push_back(temp); cout<<temp<<"> "; sumv+=temp; } cout<<endl; knapsack(); cout<<maxv<<endl; }
7.最大团问题
A.优先级队列式分支限界
#include<iostream> #include<vector> #include<queue> using namespace std; struct Node { int i; int cnum;//该节点所在团,当前拥有的个数 vector<int> x; //该节点所在团成员 }; struct cmp { bool operator()(Node a,Node b) { return a.cnum<b.cnum; } }; vector< vector <int> > v; int num; bool judge(const vector<int> &d,int xi) { for(int i=0;i<xi;i++) { if(d[i]==1&&v[i][xi]==0) { return false; } } return true; } int maxclique(vector<int> &x) { priority_queue<Node,vector<Node>,cmp> q; Node t; t.i=-1; t.cnum=0; t.x=x; q.push(t); while(!q.empty()&&q.top().i<num-1) { Node te=q.top(); q.pop(); //0 t.i=te.i+1; t.cnum=te.cnum; t.x=te.x; q.push(t); //1 if(judge(te.x,te.i+1)) { t.i=te.i+1; t.cnum=te.cnum+1; vector<int> tempx=te.x; tempx[t.i]=1; t.x=tempx; q.push(t); } } for(int i=0;i<num;i++) { if(q.top().x[i]==1) { cout<<i+1<<" "; } } cout<<endl; return q.top().cnum; } int main() { vector<int> x; cin>>num; for(int i=0;i<num;i++) { vector<int> temp; for(int j=0;j<num;j++) { temp.push_back(0); } v.push_back(temp); x.push_back(0); } for(int i=0;i<num;i++) { for(int j=i+1;j<num;j++) { int temp; cin>>temp; v[i][j]=temp; } } cout<<maxclique(x)<<endl; }
相关文章推荐
- 【DP】permu
- jQuery获取Select选择的Text和 Value(转)
- Java中关于OOM的场景及解决方法
- hadoop自定义输出文件名
- mtd-utils
- 传智播客c#学习笔记1
- WebService (一)入门
- Java中public static void main(String args [])的作用
- Backbone初步
- Qt: QT + openCV 实现摄像头采集以及拍照功能
- Web前端之jQuery 的10大操作技巧
- 将搜狗词库(.scel格式)转化为txt格式
- 面向对象三大特性
- Internet Download Manager has been registered with a fake Serial Number
- 数据存储与访问
- openwrt之ubus
- Tomcat启动内存设置
- JSF与JSP的区别
- 获取文件属性-fstat/stat
- ServiceManager守护进程的注册