0.概念
分支限界法常以广度优先(队列式(先进先出)分支限界)或以最小耗费(最大效益)优先的方式(优先队列分支限界)搜索问题的解空间树。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。
1.与回溯法的不同
2.广度优先或以最小耗费优先的方式求解目标:回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。
搜索方式的不同:回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。
广度优先:与层次遍历相似,唯一的不同之处在于不再遍历不可行节点
最小耗费优先与贪心算法的不同:
从字面上的意思理解,最小耗费优先,是以最小耗费优先计算,并不是不将较大耗费的节点作为活节点了,只是在作为活节点的时候将较大耗费的那支给剪了;而贪心算法,是只计算最小耗费的节点,较大耗费的节点就不再作为活节点了。
A算法(启发式搜索)、贪心、分支限界求最优解的区别:
对当前优先搜索方向的判断标准为< 有可能的最优解 >。而最优解可以用一个评估函数来做,即已经有的耗散度加上以后有可能的耗度。A算法就是把两个耗散度加在一起,作为当前状态的搜索搜索方向;但是对以后的耗散度的评估是麻烦的,D(迪杰斯特拉,贪心)算法就是把当前有的路的最短的作为,以后耗散度的评估。分支限界算法就是只以以前的耗散度为评估函数。
3.单源最短路径
A.优先队列式分支界限
#include
#include #include #include using namespace std; vector< vector > v; vector 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 ,cmp> q; Node startv; startv.i=start; startv.len=0; q.push(startv); dist[start]=0; while(true) { for(int j=0;j >num; for(int i=0;i temp1; for(int j=0;j >temp2;//-1表示没有边,正数表示存在边 temp1.push_back(temp2); } v.push_back(temp1); dist.push_back(INT_MAX); } shortest(0); cout<
4.装载问题
子集树
A.队列式分支限界
主要就是用队列层次遍历,当然过程中会剪掉一些明显的不合条件的分支
#include
#include #include #include using namespace std; struct Node { int i; int w;//到此为止的总重量 }; vector 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 q; Node temp; temp.i=0; temp.w=0; q.push(temp);//无效根节点 while(!q.empty()&&q.front().i 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< B.优先队列式分支限界
注意此处优先级的设置是当前载重量+剩余未选择货物的总重量
C.比较#include
#include #include #include 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 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 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 ,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>>c1>>c2; for(int i=0;i<=num;i++)//0作为无效根节点 { int temp=random(1,50); sumw+=temp; w.push_back(temp); cout<
队列式分支界限法,是将整个解空间树给搜索了一遍,效率比较低。实验测得:当n=10,c1=200,c2=200,wi随机产生时,队列式分支限界的循环次数为1022次,优先队列分支限界的循环次数是53次。
5.TSP
排列树
A.队列式分支限界
B.优先队列分支限界#include
#include #include #include #include using namespace std; vector< vector > v; vector x; int num; int bestcost; struct Node { int i; int cost; vector x; }; int random(int s,int e) { return s+rand()%(e-s); } void tsp(int start) { queue 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; for(int i=0;i temp; for(int j=0;j 优先级是当前路径总量+未排序节点最小出路总量
#include"tsp.h" using namespace std; struct Node { int i;//第i层 int ncost;//当前花费 int rcost;//剩余节点的最小出边和 int lcost;//优先级,为当前花费和剩余节点的最小出边和之和 vector
x; }; struct cmp { bool operator()(Node a,Node b) { return a.lcost>b.lcost; } }; void branchboundPQ(const vector< vector > &v,vector &x,int &costbest,int firstcity) { swap(x[firstcity],x[0]); int minsum=0;//最小出边和 vector minout;//每个顶点的最小出边 int num=x.size(); for(int i=0;i 0&&v[x[i]][x[j]] ,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
6.0-1背包
子集树
A.队列式分支限界
B.优先队列式分支限界#include
#include #include #include using namespace std; struct Node { int i; int w;//到i为止的总重量 int v;//到i为止的总价值 }; int num,c,maxv; vector w; vector 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 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>>c; maxv=-1; for(int i=0;i 优先级:当前总价值+剩余未选择物品总价值
#include
#include #include #include using namespace std; struct Node { int w;//当前重量 int v;//当前总价值 int i;//层号 int p;//优先级:当前价值+剩余未抉择物品价值 }; struct cmp { bool operator()(Node a,Node b) { return a.p w; vector v; int num,c,maxv,sumv; int random(int s,int e) { return s+rand()%(e-s); } void knapsack() { vector r; priority_queue ,cmp> q; for(int i=0;i maxv) { q.push(t); } } } } maxv=q.top().v; } int main() { maxv=-1; sumv=0; cin>>num>>c; for(int i=0;i "; sumv+=temp; } cout<
7.最大团问题
A.优先级队列式分支限界
#include
#include #include using namespace std; struct Node { int i; int cnum;//该节点所在团,当前拥有的个数 vector x; //该节点所在团成员 }; struct cmp { bool operator()(Node a,Node b) { return a.cnum > v; int num; bool judge(const vector &d,int xi) { for(int i=0;i &x) { priority_queue ,cmp> q; Node t; t.i=-1; t.cnum=0; t.x=x; q.push(t); while(!q.empty()&&q.top().i tempx=te.x; tempx[t.i]=1; t.x=tempx; q.push(t); } } for(int i=0;i x; cin>>num; for(int i=0;i temp; for(int j=0;j >temp; v[i][j]=temp; } } cout<