一、概述
分支限界法类似于回溯法。
分支限界法思路 的简单描述:把问题的解空间转化成了图或者树的结构表示,然后使用广度优先或以最小耗费(最大效益)优先的搜索策略进行遍历,遍历的过程中记录和寻找一个可行解或者最优解。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。
常见的两种分支限界法:
1)队列式(FIFO)分支限界法:按照队列先进先出(FIFO)的原则,选取下一个结点为扩展结点。
2)优先队列式分支限界法:按照优先队列中规定的优先级选取优先级最高的结点成为扩展结点。
二、与回溯法的比较
(1)搜索方式:
- 分支限界法:以广度优先方式搜索解空间树
- 回溯法:以深度优先方式搜索解空间树
(2)求解目标:
- 分支限界法是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解
- 回溯法是找出满足约束条件的所有解
三、经典问题
单源最短路径问题
给定一个带权有向图 G=(V,E),其中每条边的权是一个实数。另外,还给定V中的一个顶点,称为源。现在要计算从源到其他所有各顶点的最短路径长度。这里的长度就是指路上各边权之和。
分析:
1) 按宽度优先策略遍历解空间树
2)在遍历过程中,对处理的每个结点i,根据界限函数,估计沿该结点向下搜索所可能达到的完全解的目标函数的可能取值范围—界限bound(i)=[dow(i), up(i)]
3)从中选择使目标函数取的极小值的结点优先进行宽度优先搜索,从而不断调整搜索方向,尽快找到问题解。
注:在每次分支后,对凡是界限超出已知可行解值那些子集不再做进一步分支。这样,解的许多子集(即搜索树上的许多结点)就可以不予考虑了,从而缩小了搜索范围。
将这个图转化成树的形式:
求解:
1)节点1入队列,Q={1}。我们取出队头节点,作为扩散节点,更新他的后代的值。此题中更新节点2,3,4 的距离,并将他们加入队列,Q={1,2,3,4}。 完成后节点1出队。Q={2,3,4}。
2)同样,重复1的步骤,Q={3,4,5,6};
3)当我们取到节点3时,我们发现1-3-6的距离为11,大于1-2-6这条路径的权重,所以1-3-6这条路径之后我们不再考虑。 这就是“限界”(称为”剪枝“)的思想。
4)重复步骤,直到Q为空。优先队列法方法和FIFO方法类似,区别在于优先队列每次取队列元素中到源点距离最短的点。
//构造整个图的数据结构
typedef struct {
VerType vexs[100]; //顶点
AdjMaxtrix edgs; //边
int vexnum; //顶点数
int edgnum; //边数
} Graph;
void ShortestPaths(int v)
{
int k = 0; //从首个顶点开始访问
int t;
G.vexs[v].length = 0;
q.push(G.vexs[v].data);
while(!q.empty())
{
t = q.front();
k = NextAdj(t,k); //寻找下一结点
while(k != 0)
{
if(G.vexs[t].length + G.edgs[t][k].info <= G.vexs[k].length) //剪枝操作
{
G.vexs[k].length = G.vexs[t].length + G.edgs[t][k].info; //最短路径
q.push(G.vexs[k].data);
}
k = NextAdj(t,k);
}
q.pop();
}
}
来源:CSDN
作者:Fighting_初心
链接:https://blog.csdn.net/qq_34519487/article/details/103959466