布线问题
题目描述:印刷电路板将布线区域划分成 n×m 个方格阵列,要求确定连接方格阵列中的方格 a 的中点到方格 b 的中点的最短布线方案。在布线时,电路只能沿直线或直角布线,为了避免线路相交,已布了线的方格做了封锁标记,其他线路不允许穿过被封锁的方格。
扫描二维码关注公众号,回复:
17448399 查看本文章
![]() |
|||||||||
分析
(1)根据题意,可以把起点作为根结点,从根结点开始,按照上、下、左、右四个方向扩展子结点,向外扩散,逐渐靠近目标。
(2)耗散每一次扩展都相同,视为移动 1 步,距离加 1 。而且,在使用队列式分支限界时,任意一个位置第一次访问到该位置时的长度即为最优。
注意:当使用优先队列分支限界时需要比较每一个位置新旧不同的距离,保存小的,因为优先队列是按照结点优先级排序扩展结点,打破了原有的树的深度。
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
1 |
2 |
0 |
-1 |
0 |
0 |
-3 |
0 |
2 |
1 |
-2 |
1 |
-1 |
-1 |
0 |
0 |
0 |
0 |
0 |
-1 |
1 |
2 |
0 |
-1 |
0 |
0 |
0 |
0 |
0 |
-1 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
剪枝函数设计
队列式分支限界法:
(1)当达到边时,不再扩展;
(2)当遇到障碍物时不再扩展;
(3)某个结点访问过,后面经过该节点的分枝全都减掉;
优先队列式分支限界法:
(1)当达到边时,不再扩展;
(2)当遇到障碍物时不再扩展;
(3)当途中某一位置出现更优距离时旧的分枝子结点全部处死。
(4)估价函数:某点的价值估价 = 该点已发生的距离 + 该点到目标位置的距离(该点到目标位置的距离 = 水平距离 + 竖直距离)
程序代码
/* 基于链队数据结构+队列式(FIFO)分支限界算法的布线问题的求解(C语言版)*/
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define PERROR -2
typedef int Status;
// 数据元素的类型,使用时需根据问题的需求定义。
typedef struct {
int row; // 数据域
int col; // 指针域
int f; // 结点距离起点的距离
int move_mode; // 0: 起点 1:向上 2:向下 3:向左 4:向右
} ElemType;
// 结点类型定义
typedef struct LQNode {
ElemType data; // 数据域
struct LQNode *next; // 指针域
} LQNode, *LQNodePtr; // 结点及指针类型
// 链队结构类型定义
typedef struct {
LQNodePtr front; // 队头指针
LQNodePtr rear; // 队尾指针
} LQueue; // 链队类型
// 1、链队初始化
Status InitQueue_LQ(LQueue &Q) {
Q.front = (LQNode *)malloc(sizeof(LQNode)); // 申请头结点存储空间
if (Q.front == NULL) return OVERFLOW; // 内存分配失败
Q.rear = Q.front;
Q.front->next = NULL; // 将头结点指针域置为 NULL
return OK;
}
// 2、销毁链队
Status DestroyQueue_LQ(LQueue &Q) {
if(Q.front==NULL)return PERROR; // 参数合法性检验,参数非法或已销毁,返回不再执行销毁操作
//从头结点开始,依次队每一个结点释放内存,这里,Q.rear辅助向后传递的一个临时指针使用
while (Q.front!=NULL) {
Q.rear = Q.front->next;
free(Q.front);
Q.front = Q.rear;
}
Q.front = NULL; // 避免指针悬挂
Q.rear = NULL; // 避免指针悬挂
return OK;
}
// 3、判断链队 Q 是否为空
Status QueueEmpty_LQ(LQueue Q) {
if(Q.front == NULL)return PERROR; // 参数合法性检验
if(Q.front == Q.rear) {
return TRUE;
} else {
return FALSE;
}
}
// 4、清空链队 Q
Status ClearQueue_LQ(LQueue &Q) {
if(Q.front==NULL)return PERROR; // 参数合法性检验,非法返回参数错误
Q.front->next = NULL; // 避免指针悬挂
Q.rear = Q.front;
return OK;
}
// 5、入队操作
EnQueue_LQ(LQueue &Q, ElemType e) {
if(Q.front==NULL)return PERROR; // 参数合法性检验,非法返回参数错误
LQNodePtr p;
p = (LQNode *)malloc(sizeof(LQNode));
if(p == NULL) return OVERFLOW; // 内存分配失败
p->data = e;
p->next = NULL;
if(Q.front == NULL) { // 如果队空
Q.front->next = p;
} else {
Q.rear->next = p; // 如果队非空,在队尾添加
}
Q.rear = p;
return OK;
}
// 6、出队操作
Status DeQueue_LQ(LQueue &Q, ElemType &e) {
if(Q.front==NULL)return PERROR; // 参数合法性检验,非法返回参数错误
LQNodePtr p;
if(Q.rear == Q.front)return ERROR; // 如果队空,返回,不执行操作
p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if(Q.front->next == NULL) {
Q.rear = Q.front; // 如果删除后队空,移动队尾指针到头结点
}
free(p);
return OK;
}
// 7、读取队头元素,并用 e 返回
Status GetHead_LQ(LQueue Q, ElemType &e) {
if(Q.front==NULL)return PERROR; // 参数合法性检验,非法返回参数错误
if(Q.front == Q.rear)return ERROR; // 如果是空队,无法读取,返回错误
LQNodePtr p = Q.front->next;
e = p->data; // 队头元素交给 e
return OK;
}
Status searchPath(int *paramap,int n,int m) {
int i, j; // 声明循环用变量
ElemType e, temp; // 元素类型变量
LQueue Q; // 创建队列 Q
int distance = -1; // 距离起点的距离,无方案时距离为 -1
// 从地图参数数组里获取相应信息,以提高程序可读性
int map[n][m],result[n][m]; // 二维地图
int start_row, start_col, end_row, end_col; // 地图起点和终点信息
for(i=0; i<n; i++) {
for(j=0; j<m; j++) {
result[i][j] = 0;
map[i][j] = paramap[i*m+j]; // 解析参数
if(map[i][j]==-2) {
start_row = i; // 获取地图的起点信息
start_col = j;
} else if(map[i][j]==-3) {
end_row = i; // 获取地图的终点信息
end_col = j;
}
}
}
InitQueue_LQ(Q); // 初始化队列 Q
e.row = start_row; // 从起点开始
e.col = start_col;
e.f = 0;
e.move_mode = 0;
EnQueue_LQ(Q, e);
while(FALSE == QueueEmpty_LQ(Q)) { // 队列非空执行循环
DeQueue_LQ(Q, e);
if((end_row == e.row)&&(end_col == e.col)) { // 是目标则退出
printf("已成功为您找到方案!\n");
distance = e.f;
printf("最小距离为:%d \n", distance);
break;
}
// 往上扩展结点
if(e.row>0) {
temp.row = e.row -1;
temp.col = e.col;
temp.f = e.f + 1;
temp.move_mode = 2;
if(map[temp.row][temp.col] == 0) { // 未访问过的结点
map[temp.row][temp.col] = temp.f;
result[temp.row][temp.col] = temp.move_mode;
EnQueue_LQ(Q, temp); // 将子结点入队
} else if(map[temp.row][temp.col] == -3) {
result[temp.row][temp.col] = temp.move_mode;
EnQueue_LQ(Q, temp); // 将子结点入队
}
}
// 往右扩展结点
if(e.col < m-1) {
temp.row = e.row;
temp.col = e.col + 1;
temp.f = e.f + 1;
temp.move_mode = 3;
if(map[temp.row][temp.col] == 0) { // 未访问过的结点
map[temp.row][temp.col] = temp.f;
result[temp.row][temp.col] = temp.move_mode;
EnQueue_LQ(Q, temp); // 将子结点入队
} else if(map[temp.row][temp.col] == -3) {
result[temp.row][temp.col] = temp.move_mode;
EnQueue_LQ(Q, temp); // 将子结点入队
}
}
// 往下扩展结点
if(e.row < n-1) {
temp.row = e.row + 1;
temp.col = e.col;
temp.f = e.f + 1;
temp.move_mode = 1;
if(map[temp.row][temp.col] == 0) { // 未访问过的结点
map[temp.row][temp.col] = temp.f;
result[temp.row][temp.col] = temp.move_mode;
EnQueue_LQ(Q, temp); // 将子结点入队
} else if(map[temp.row][temp.col] == -3) {
result[temp.row][temp.col] = temp.move_mode;
EnQueue_LQ(Q, temp); // 将子结点入队
}
}
// 往左扩展结点
if(e.col > 0) {
temp.row = e.row;
temp.col = e.col - 1;
temp.f = e.f + 1;
temp.move_mode = 4;
if(map[temp.row][temp.col] == 0) { // 未访问过的结点
map[temp.row][temp.col] = temp.f;
result[temp.row][temp.col] = temp.move_mode;
EnQueue_LQ(Q, temp); // 将子结点入队
} else if(map[temp.row][temp.col] == -3) {
result[temp.row][temp.col] = temp.move_mode;
EnQueue_LQ(Q, temp); // 将子结点入队
}
}
}
if((true == QueueEmpty_LQ(Q))&&( -1 == distance)) { // 仿制恰巧目标节点出队后队空的情况
printf("当前地图无法到达指定目的地!\n");
return ERROR;
}
int path[distance]; // 用以存储动作方案
i = distance;
path[distance-1] = 0;
int p = e.row;
int q = e.col;
while(result[p][q]!= 0) { // 导出动作序列,以生成行动方案
i--;
if(result[p][q] == 1) {
path[i] = 2;
p--;
} else if(result[p][q] == 2) {
path[i] = 1;
p++;
} else if(result[p][q] == 3) {
path[i] = 4;
q--;
} else if(result[p][q] == 4) {
path[i] = 3;
q++;
}
}
printf("移动方案为:\n");
for(i=0; i<distance; i++) {
if(path[i]==1) {
printf("第 %d 步 :向上\n", i+1);
} else if(path[i]==2) {
printf("第 %d 步 :向下\n", i+1);
} else if(path[i]==3) {
printf("第 %d 步 :向左\n", i+1);
} else if(path[i]==4) {
printf("第 %d 步 :向右\n", i+1);
}
}
printf("\n");
/* 输出地图填充计算分析过程 */
printf("地图计算过程分析如下:\n");
for(i=0; i<n; i++){ // 将方案传回
for(j=0; j<m; j++){
printf("%d\t", map[i][j]);
}
printf("\n");
}
// 将地图填充计算分析过程传回
for(i=0; i<n; i++) {
for(j=0; j<m; j++) {
paramap[i*m+j] = map[i][j];
}
}
DestroyQueue_LQ(Q);
return OK;
}
int main() {
int i, j;
int n = 8; // 声明地图方格行数
int m = 10; // 声明地图方格列数
int map[n][m]; // 声明地图方格阵列
int para[n*m]; // 传参用一维数组
for(i=0; i<n; i++) { // 初始化地图方格阵列,0 表示距离为 0;
for(j=0; j<m; j++) {
map[i][j] = 0;
}
}
map[3][3] = -2; // 定义起点位置,为测试方便,写死了
map[2][9] = -3; // 定义终点位置,为测试方便,写死了
// 定义障碍物位置
map[2][6] = map[3][6] = map[4][6] = map[3][5] = map[4][2] = map[5][2] = map[6][2] = -1;
// 将二维地图数组转换为一维数组,方便传参
for(i=0; i<n; i++) {
for(j=0; j<m; j++) {
para[i*m+j] = map[i][j];
}
}
searchPath(para,n,m);
return 0;
}
运行结果