问题描述
印刷电路板将布线区域划分成n*m个方格阵列,精确的电路布线问题要求确定连接方格a的中点到方格b的中点的最短布线问题。在布线时,电路只能沿着直线或直角布线。为了避免线路相交,已布了线的方格做了封锁标记,其他线路不允许穿过被封锁的方格。
问题分析
布线问题的解空间是一个图结构。
解决的思路采用分支限界法,从起始节点开始不断扩展,直到目的节点结束,得到最短的路径长度(即最优值);然后从目的节点往回回溯,逐步求得最优解。
解题步骤
- 创建一个表示节点的结构体Position,其包含两个int变量,x,y分别表示当前节点所在的行数,列数;
- 创建一个二维数组grid[][],表示当前的电路板,初始时,grid[i][j]=0表示可以布线,grid[i][j]=1表示该节点被封锁,不能布线。为了便于处理方格边界的问题,约定在原有的方格阵列四周增设一道围墙,这些附加方格全部标记为1,不允许扩展。
- 创建一个队列Q,用来扩展活结点表,初始时队列为空,将起始节点的四周的可扩展节点全部加入队列,不断循环,求解最优值,直到队列为空为止。本题采用队列式分支限界法求解;
- 创建位移矩阵offset[][],表示当前节点向上下左右扩展的相对位移
输入
输入共有n+3行
第一行包括两个数,n,m表示电路板是一个n*m的方格阵列
第二行包括两个数,row1,col1,表示起始点的行号和列号
第三行包括两个数,row2,col2,表示目标点的行号和列号
然后输入n行,每行m个数,0或1,表示电路板的封锁情况
数据规模:
0<=m<=100
0<=n<=100
输出
输出只有一行
表示从起始点到目的点的最短距离
样例输入
7 7
3 2
4 6
0 0 1 0 0 0 0
0 0 1 1 0 0 0
0 0 0 0 1 0 0
0 0 0 1 1 0 0
1 0 0 0 1 0 0
1 1 1 0 0 0 0
1 1 1 0 0 0 0
样例输出
9
代码
#include <iostream>
using namespace std;
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
#include<math.h>
#include<queue>
//方格结构体
typedef struct
{
int x;//x表示小方格所在行号
int y;//y表示小方格所在列号
}Position;
int grid[102][102];//用来表示电路板
int n,m;//n表示电路板行数,m表示电路板的列数
bool FindPath(Position start,Position finish,int PathLen)
{
//起点和目的点是同一个点,最短路径为0
if(start.x==finish.x&&start.y==finish.y){
PathLen=0;
return true;
}
//设置方格围墙,扩充边界
for(int j=0;j<=m+1;j++)
grid[0][j]=1;//第0行全部置1
for(int i=0;i<=n+1;i++)
grid[i][0]=1;//第0列全部置1
for(int j=0;j<=m+1;j++)
grid[n+1][j]=1;//第n+1行全部置1
for(int i=0;i<=n+1;i++)
grid[i][m+1]=1;//第m+1列全部置1
//初始化相对位移,上,下,左,右
Position offset[4];
offset[0].x=0;offset[0].y=1;//表示向右走一格
offset[1].x=1;offset[1].y=0;//表示向下走一格
offset[2].x=0;offset[2].y=-1;//表示向左走一格
offset[3].x=-1;offset[3].y=0;//表示向上走一格
int NumOfNbrs=4;//可扩展的点为四连通区域
Position now,nerb;//now表示当前的活结点,nerb表示与活结点相邻的上下左右四个方格
now=start;//给now赋初值,从初始节点开始,注意start不在活结点表里面
grid[start.x][start.y]=0;//初始节点到初始节点的
//构建活结点表的队列
queue<Position>Q;
while(1){
for(int i=0;i<NumOfNbrs;i++){
nerb.x=now.x+offset[i].x;
nerb.y=now.y+offset[i].y;
//说明当前节点可以扩展,那么填充并加入活结点表
if(grid[nerb.x][nerb.y]==0){
grid[nerb.x][nerb.y]=grid[now.x][now.y]+1;
//如果是目的节点,退出
if(nerb.x==finish.x&&nerb.y==finish.y)
break;
Q.push(nerb);//将新扩展的节点加入活节点表
}
}
if(nerb.x==finish.x&&nerb.y==finish.y)
break;//如果是目的节点,退出
if(Q.empty()) return false;//当队列为空时,退出
now=Q.front();//更新now的值,不然没法接着遍历新的活结点
Q.pop();//将这个节点从活结点表中删除
}
return true;//返回
}
int main()
{
cin>>n>>m;//输入电路板的行数,列数
Position start,finish;//表征起始方格,目的方格
cin>>start.x>>start.y;
cin>>finish.x>>finish.y;
//根据输入初始化电路板grid
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>grid[i][j];
int Pathlen;//用来传递参数,当路径不为0时,不用Pathlen
//执行FindPath函数,更新grid数组,并判断路径是否为0
FindPath(start,finish,Pathlen);
//输出结果
if(Pathlen==0)
cout<<0;
else
cout<<grid[finish.x][finish.y];
return 0;
}