分支限界法类似于回溯法,也是在问题的解空间上搜索问题的解的算法。分支限界法是找出满足约束条件的一个解或者满足某种条件的最优解。分支限界法则以广度优先或者最小耗费优先的方式搜索解空间。
其搜索策略是:
1、在扩展结点处,先生成其所有的儿子结点,然后再从当前的活结点表中选择下一个扩展结点。
2、为了加速搜索的进程,在每一个活结点处,计算一个函数值,并根据函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着最有利的分支推进。
分支限界的思想主要体现在第二点上。
从活结点表中选择下一个扩展结点的不同方式导致不同的分支限界法,最常见的有以下两种
1、队列式分支限界法
2、优先队列式分支限界法
下面结合布线问题来体会分支限界的思想:代码
#include "stdafx.h" #include<queue> #include<iostream> using namespace std; //全局变量 int grid[7][7]; //表示方格上位置的类 struct Position { int row; int col; }; //分支限界算法 bool FindPath(Position start,Position finish,int& PathLen,Position *&path) { //找到最短布线路径,则返回真,否则返回假 if((start.row==finish.row) && start.col==finish.col) { PathLen=0; return true; } //设置方格阵列的围墙 for(int i=0;i<7;i++) grid[0][i]=grid[6][i]=1; for(int i=0;i<7;i++) grid[i][0]=grid[i][6]=1; //设置方向移动坐标值:东、南、西、北 Position offset[4]; offset[0].row=0;offset[0].col=1; //东 offset[1].row=1;offset[1].col=0; //南 offset[2].row=0;offset[2].col=-1; //西 offset[3].row=-1;offset[3].col=0; //北 //相邻的方格数 int NumNeighBlo=4; Position here,nbr; //设置当前方格,即搜索单位 here.row=start.row; here.col=start.col; //由于0和1用于表示方格的开放和封锁,故距离:2-0 3-1 grid[start.row][start.col]=2; //队列式搜索,标记可达相邻方格 queue<Position> q_FindPath; do { for(int i=0;i<NumNeighBlo;i++) { //达到四个方向 nbr.row=here.row+offset[i].row; nbr.col=here.col+offset[i].col; if(grid[nbr.row][nbr.col]==0) { //该方格未标记 grid[nbr.row][nbr.col]=grid[here.row][here.col]+1; if((nbr.row==finish.row)&&(nbr.col==finish.col)) break; //将这个邻居入队 q_FindPath.push(nbr); } } //是否到达目标位置finish if((nbr.row==finish.row)&&(nbr.col==finish.col)) break; //活结点队列是否为空 if(q_FindPath.empty())return false; //无解 //访问对首元素出队 here=q_FindPath.front(); q_FindPath.pop(); }while(true); //构造最短布线路径 PathLen=grid[finish.row][finish.col]-2; path=new Position[PathLen]; //路径 //从目标位置finish开始向起始位置回溯 here=finish; for(int j=PathLen-1;j>=0;j--) { path[j]=here; //找前驱位置 for (int i = 0; i <=NumNeighBlo; i++) { nbr.row=here.row+offset[i].row; nbr.col=here.col+offset[i].col; if(grid[nbr.row][nbr.col]==j+2) //距离加2正好是前驱位置 break; } here=nbr; } return true; } int _tmain(int argc, _TCHAR* argv[]) { //最简单情况分析和单步调试是解决问题两大方法 cout<<"---------分支限界法之布线问题--------"<<endl; int path_len; Position *path; Position start,finish; start.row=2;start.col=1; finish.row=5;finish.col=3; if(FindPath(start,finish,path_len,path)) { for(int i=0;i<path_len;i++) cout<<"("<<path[i].row<<","<<path[i].col<<")"<<endl; } else { cout<<"没有找到路径"<<endl; } return 0; }
测试结果:
---------分支限界法之布线问题--------
(3,1)
(4,1)
(5,1)
(5,2)
(5,3)