分支限界法

本文详细介绍了如何使用分支限界法解决单源最短路径问题和最大团问题。在最短路径问题中,通过定义解空间、确定解空间结构、广度优先搜索并利用限界函数来找到最短路径。在最大团问题中,同样利用分支限界法,通过0-1向量表示解,动态构建子集树,使用最大堆优先级队列进行搜索。文章还提供了算法实现的详细描述和数据结构的设计。
摘要由CSDN通过智能技术生成

1、单源最短路径问题: 给定一个带权有向图G=(V,E),其中每条边的权是一个非负实数,V={1,2,...,n}。设顶点v作为源顶点。要计算从源到所有其他各顶点的最短路径长度。
  Dijkstra算法是解决这个问题的一个著名的贪心算法。这个问题也适合用分支限界法来求解。分支限界法类似于回溯法,也是在问题的解空间树(对这里的问题则为图)上搜索问题的解。但分支限界法采用广度优先或最小耗费优先的方式来搜索,即遍历到一个结点之后,接着就遍历其相邻的所有结点。这就意味着需要一个队列结构来完成树或图的遍历。初始时根结点入队,按结点的出队顺序进行遍历,遍历到一个结点时,就让其相邻的所有结点入队。搜索过程直到队列变空为止。对树来说,广度优先搜索就相当于层次遍历。常用的队列有先进先出(FIFO)队列、优先级队列。与一般的广度优先遍历不同,分支限界法通常可以使用限界函数(即剪枝函数)来计算结点的值,以剪去导致无效解或非最优解的子树,即让不可行的结点不入队,这样就不会搜索到以不可行结点为根的子树。由于使用队列结构来进行遍历,因此分支限界法通常不需要递归,这可以节省一些递归展开的时间开销。但它需要一个额外的队列结构,我们知道递归展开则使用了栈结构,可见与回溯法相比,分支限界法并不占太大的便宜。
  (1)定义问题的解空间:这里的解是源顶点到其他各顶点的最短路径。我们可以用数组prev来记录从源到各顶点的最短路径上前驱顶点,即prev[i]是从源到顶点i的最短路径上i的前一个顶点。这样prev[i]=i1, prev[i1]=i2, ..., prev[ik]=v(到达源顶点v),就可以找到从源顶点v到任意顶点的i的最短路径"1-->ik-->...-->i2-->i1-->i"。我们用数组dist记录源到各顶点最短路径的长度。初始时,dist[i]为源顶点v到顶点i的边上的权值,prev[i]为源顶点v,若没有直接相连的边,则(v,i)用一个充分大的值NoEdge表示,且prev[i]=0,表示无前驱。
  (2)确定解空间的结构:这里的解空间树以源顶点为根,包含源顶点到每一个顶点的所有可能的路径。这里可以直接对图进行遍历。图用邻接矩阵表示。
  (3)以广度优先方式搜索整个解空间,找出所要的解:这里是求从源到各顶点的最短路径,因此要使用最小堆表示的优先级队列来进行广度优先搜索。入队的顶点必须还要附带一个优先级域,队列根据这个域来判定结点的出队优先级。这里要使用从源到该顶点的最短路径长度作为优先级域。初始时源结点入队。对队列中所有活结点,每次总是选取从源到这些结点中路长最短的结点E出队,以作为当前扩展结点,然后检查与E相邻的所有结点,这时我们并不让它们都入队,而是通过限界函数来剪去一些结点。只有从顶点E到顶点j有边可达,且从源出发途经顶点E到j的路径长度小于当前最优路长(记录在dist[j]中),才会把这个顶点j插入到队列中,并设置j的前驱结点为E。不满足这个限界条件的相邻顶点都将被剪去。由于每次选取的途经结点E都是离源的路径长度最短,因此能保证最后得到的所有路径长度都是最短的。
  (4)分支限界法的数据结构描述:整个问题包括解空间树的结点信息,用于构造最优解的数据成员、可选的限界函数,分支限界法搜索函数BranchAndBound(i),算法实现函数等。通常用一个类来描述这些信息,算法实现函数也可以一个独立的全局函数。优先级队列中的结点需要附带优先级信息,用一个独立的类MinHeapNode来描述队列中的结点信息。对于单源最短路径问题,数据结构描述如下:

[cpp]  view plain   copy
  1. //问题及其解空间描述  
  2. template<class Type>  
  3. class ShortestPathsProblem{  
  4. public:  
  5.     friend void ShortestPaths(int,Type**,int*,Type*,int,Type); //算法实现函数  
  6.     void BranchAndBound(int);  //分支限界搜索函数  
  7. private:  
  8.     int n;  //图G的顶点数  
  9.     int* prev;  //前驱顶点数  
  10.     Type** c;  //图G的邻接矩阵  
  11.     Type* dist; //最短距离数组  
  12.     Type NoEdge;  //无边标记,一个充分大的值  
  13. };  
  14. //优先级队列的结点描述  
  15. template<class Type>  
  16. class MinHeapNode{  
  17. public:  
  18.     friend ShortestPathProblem<Type>;  
  19.     operator int() const//优先级队列通过本函数来判定结点的优先级,以进行出队操作  
  20.         return length;  
  21.     }  
  22. private:  
  23.     int i; //顶点  
  24.     Type length;  //从源到本顶点的最短路长作为优先级  
  25. };  

  限界函数:有边可达的条件为c[E.i][j]<NoEdge。从源出发途经顶点E到j的路径长度小于当前最优路长的条件为E.length+c[E.i][j]<dist[j],这两个条件同时满足时,结点j才会被插入到队列中,否则剪去结点j。这两个条件比较简单,可以在分支限界法的实现函数中直接使用,因此这里并没有设计成一个独立的函数。
  分支限界搜索函数:源结点为初始的扩展结点,然后使用优先级队列来进行广度优先遍历,在遍历过程中用限界函数剪去导致无效解或非最优解的子树。函数实现如下:

[cpp]  view plain  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值