分支限界法(算法分析与设计)

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().ibestcost)
				{
					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.优先队列式分支限界

注意此处优先级的设置是当前载重量+剩余未选择货物的总重量

#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<
C.比较

队列式分支界限法,是将整个解空间树给搜索了一遍,效率比较低。实验测得:当n=10,c1=200,c2=200,wi随机产生时,队列式分支限界的循环次数为1022次,优先队列分支限界的循环次数是53次。

5.TSP

排列树

A.队列式分支限界

#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
B.优先队列分支限界

优先级是当前路径总量+未排序节点最小出路总量

#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;i0&&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.队列式分支限界

#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
B.优先队列式分支限界

优先级:当前总价值+剩余未选择物品总价值

#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;imaxv)
				{
					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<








你可能感兴趣的:(数据结构与基础算法)