问题描述:
印刷电路板将布线区域划分成n×m个方格。精确的电路布线问题要求确定连接方格a的中点到方格b的中点的最短布线方案。在布线时,电路只能沿直线或直角布线。为了避免线路相交,已布了线的方格做了封锁标记,其它线路不允穿过被封锁的方格。
电路板布线问题,对给定的电路板,找出最短的布线路径。
算法思路:
布线问题的解空间是一个排列树。采用队列式分支限界从起始位置start开始,将其作为第一个可扩展的结点。与该扩展结点相邻(即上下左右四个方向)且可达的方格成为可行结点被加入到活结点队列中,并且将这些方格标记为1,即从起始位置start到这些方格的距离为1。然后从活结点队列取出队首结点作为下一个可扩展的结点,并将与当前扩展结点相邻且未被标记过的方格标记为2,并存入活结点队列。继续执行该算法直到搜索到终止结点finish或者队列为空为止。
对于方格阵列的建立:用0表示可达且未被标记过的结点,用1表示该方格不可达。为了防止出界,可以在方格阵列外围用1建立不可达的“围墙”。由于0和1被使用过,起始位置可以从2开始,等价于将距离起始位置的标记距离都做了加2处理,那么实际距离为标记距离减2。
代码附录:
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status;
//队列的最大长度
#define MAXQSIZE 20
struct Position{
int row; // 行
int col; // 列
};
typedef Position QElemType;
// 循环队列
typedef struct{
QElemType *base;
int front;
int rear;
}SqQueue;
//实现队列的一般功能
Status InitQueue(SqQueue &Q){
Q.base = (QElemType*)malloc(MAXQSIZE*sizeof(QElemType));
if(!Q.base) exit(OVERFLOW);
Q.front=Q.rear=0;
return OK;
}
Status QueueEmpty(SqQueue Q){
return Q.rear==Q.front;
}
Status EnQueue(SqQueue &Q,QElemType e){
if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;
Q.base[Q.rear] = e;
Q.rear = (Q.rear+1) % MAXQSIZE;
return OK;
}
Status DeQueue(SqQueue &Q,QElemType &e){
if(Q.rear==Q.front) return ERROR;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;
return OK;
}
int n, // n行
m; // m列
int **grid; // 区域阵列
Status FindPath(Position start, Position finish, int &PathLen, Position *&Path){
if(start.col == finish.col && start.row == finish.row) {
PathLen = 0;
return TRUE;
}
// 初始化上下边界
for(int i=0; i<=m+1; i++) grid[0][i] = grid[n+1][i] = 1;
// 初始化左右边界
for(int i=0; i<=n+1; i++) grid[i][0] = grid[i][m+1] = 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 NumOfNbrs = 4;
Position here, nbr;
here.col = start.col; here.row = start.row;
grid[start.row][start.col] = 2;
// 初始化队列
SqQueue Q; InitQueue(Q);
do{
for(int i=0; i<NumOfNbrs; 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;
EnQueue(Q,nbr);
}
}
if(nbr.row == finish.row && nbr.col == finish.col) break;
// 队列是否为空
if(QueueEmpty(Q)) return FALSE;
DeQueue(Q,here);
}while(TRUE);
// 构造路径
PathLen = grid[finish.row][finish.col] - 2;
Path = new Position[PathLen];
here = finish;
for(int j=PathLen-1; j>=0; j--){
Path[j] = here;
for(int i=0; i<NumOfNbrs; i++){
nbr.row = here.row + offset[i].row;
nbr.col = here.col + offset[i].col;
if(grid[nbr.row][nbr.col] == j+2) break;
}
here = nbr;
}
return TRUE;
}
测试用例:
运行结果:
补充:
int main(){
int pathLength;
Position start = {3,2}; // 起点
Position finish = {4,6}; // 终点
Position* path;
FILE *file = fopen("test.txt","r");
fscanf(file,"%d",&n);
fscanf(file,"%d",&m);
grid = new int *[n+2];
for(int i = 1; i < n+2; i++){
grid[i] = new int [n+2];
}
for(int i = 1;i<=n;i++){
for(int j = 1; j<=n; j++) fscanf(file,"%d",&grid[i][j]);
}
fclose(file);
FindPath(start,finish,pathLength,path);
printf("从起始位置到结束位置经历的最少方格数:%d\n",pathLength);
printf("从起始位置到结束位置经历的方格:");
for(int i = 0; i < pathLength; i++){
printf("(%d,%d) ",path[i].row,path[i].col);
}
delete grid;
return 0;
}
test.txt:
7
7
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