布线问题
题目描述:印刷电路板将布线区域划分成 n×m 个方格阵列,要求确定连接方格阵列中的方格 a 的中点到方格 b 的中点的最短布线方案。在布线时,电路只能沿直线或直角布线,为了避免线路相交,已布了线的方格做了封锁标记,其他线路不允许穿过被封锁的方格。
效果对比
队列式分支限界算法在搜索过程扩展或考查的点数如下图所示:
6 |
5 |
4 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
5 |
4 |
3 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
4 |
3 |
2 |
1 |
2 |
3 |
-1 |
7 |
8 |
-3 |
3 |
2 |
1 |
-2 |
1 |
-1 |
-1 |
8 |
9 |
0 |
4 |
3 |
-1 |
1 |
2 |
3 |
-1 |
7 |
8 |
9 |
5 |
4 |
-1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
6 |
5 |
-1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
7 |
6 |
5 |
4 |
5 |
6 |
7 |
8 |
9 |
0 |
采用优先队列后的效果如下图所示,由此可见,大部分无用的分支没有被扩展就找到目标结点了。
0 |
0 |
0 |
0 |
0 |
5 |
6 |
7 |
0 |
0 |
0 |
0 |
0 |
0 |
3 |
4 |
5 |
6 |
7 |
0 |
0 |
0 |
0 |
1 |
2 |
3 |
-1 |
7 |
8 |
-3 |
0 |
0 |
1 |
-2 |
1 |
-1 |
-1 |
8 |
9 |
0 |
0 |
0 |
-1 |
1 |
2 |
0 |
-1 |
0 |
0 |
0 |
0 |
0 |
-1 |
0 |
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 |
程序代码和运行效果截图放在了下面,其他关于题目的分析过程和说明请参照我之前发的帖子:
基于链队数据结构+队列式(FIFO)分支限界算法的布线问题的求解(C语言版)
程序代码:
/* 基于链表数据结构 + 优先队列式(LC)分支限界算法的布线问题的求解(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 calculate; // 预估距离目标结点的最少距离
int move_mode; // 0: 起点 1:向上 2:向下 3:向左 4:向右
} ElemType;
// 结单链表点类型定义
typedef struct LNode {
ElemType data; // 数据域
struct LNode *next; // 指针域
} LNode;
// 链表结构体类型定义
typedef LNode* LinkList;
// 1、链队初始化,初始化一个只含头结点的空单链表 L
Status InitList_L (LinkList &L) {
L = (LNode*)malloc(sizeof(struct LNode)); // 为头结点申请空间
if(L == NULL) return OVERFLOW; //如果申请失败,返回溢出
L->next = NULL;
return OK;
}
// 2、构造单链表结点
LNode* MakeNode_L(ElemType e) {
LNode *p;
p = (LNode*)malloc(sizeof(struct LNode)); // 分配结点空间
if (p!=NULL) {
p->data = e;
p->next = NULL;
}
return p;
}
// 3、销毁单链表
Status DestroyList_L(LinkList &L){
LNode *q;
if(L==NULL) return ERROR; // 判断链表是否存在,已销毁,不再执行销毁操作。
while(L!=NULL) { // 如果是非空表,依次释放表中各结点
q = L;
L = q->next;
free(q);
}
return OK;
}
// 4、判断单链表是否为空
Status ListEmpty_L(LinkList L){
if(NULL == L) return ERROR; // 判断链表是否存在,已销毁,不再执行销毁操作。
if(L->next == NULL){
return TRUE;
}
else{
return FALSE;
}
}
// 5、单链表的插入 P 的后继结点操作
Status InsertAfter_L(LNode *p, LNode *q){
if(p == NULL || q == NULL) return PERROR; // 参数不合理
q->next = p->next; // 修改q结点的指针域
p->next = q;
return OK;
}
// 6、头插法往单链表中的插入一个结点
Status InsertFirst_L(LinkList &L, ElemType e){
if(L == NULL) return PERROR; // 参数不合理
LNode *p;
p = MakeNode_L(e);
if(p == NULL)return ERROR; // 内存溢出,结点创建不成功,不能执行插入操作
p->next = L->next; // 修改q结点的指针域
L->next = p;
return OK;
}
// 7、删除单链表的第 i 个结点
Status ListDelete(LinkList &L,int i,ElemType &e){
LNode *p, *q;
p = L;
int j = 0;
while(p!=NULL && j<i-1){ // 如果存在,找到第 i-1 个结点
p = p->next;
j++;
}
if(p==NULL||p->next==NULL)return ERROR; // 如果第 i-1 个结点和第 i 个结点不存在,无法删除,返回错误
q = p->next; // 用指针 q 指向第 i 个结点
e = q->data; // 将第 i 个结点的值赋给 e
p->next = q->next; // 将 q 的后继赋值给 p
free(q); // 释放结点 q 所占空间
return OK;
}
// 8、单链表的查找元素操作
LNode* Search_L(LinkList L, ElemType e) {
LNode *p;
if(L == NULL) return NULL;
p = L->next;
while(p!=NULL && p->data.calculate != e.calculate) {
p = p->next; // 访问下一个元素
}
return p;
}
// 9、在有序单链表中查找插入位置
LNode* SearchPosition_L(LinkList L, ElemType e) {
LNode *p, *q;
if(L == NULL) return NULL;
p = L; // p 用来保存插入位置的前一个结点
q = p->next; // q 指向插入点对应的结点,即在插入点之前插入 e
while(q!=NULL && q->data.calculate<e.calculate) {
p = q; // 寻找向后继续进行,p、q 均向后移动
q = p->next; // 访问下一个元素
}
return p;
}
// 10、在有序表中的正确位置插入结点 e
Status InsertNode_L(LinkList &L, ElemType e){
if(L == NULL) return PERROR; // 参数不合理
LNode *p, *q;
q = MakeNode_L(e);
if(p = NULL)return ERROR; // 内存溢出,结点创建不成功,不能执行插入操作
p = SearchPosition_L(L, e);
q->next = p->next; // 修改q结点的指针域
p->next = q;
return OK;
}
// 11、求链表的长度
Status GetListLength_L(LinkList L) {
int length = 0;
LNode *p;
if(L == NULL) return PERROR; // 参数不合理
if(L->next == NULL)return 0;
p = L->next;
while(p!=NULL){
length++;
p = p->next;
}
return length;
}
// 12、遍历单链表
Status ListTraverse_L(LinkList L, Status(*visit)(ElemType e)) {
if(L == NULL) return PERROR; // 参数不合理
if(L->next == NULL)return ERROR; // 空表不执行操作
LNode *p;
p = L->next;
while (p != NULL) {
visit(p->data);
p = p->next;
}
return OK;
}
// 13、配合遍历,依次输出表中元素
Status PrintList_L(ElemType e){
printf("%d\t",e.calculate);
return OK;
}
Status searchPath(int *paramap,int n,int m) {
int i, j; // 声明循环用变量
ElemType e, temp; // 元素类型变量
LinkList L; // 创建队列 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;
}
}
}
InitList_L (L); // 初始化队列 Q
e.row = start_row; // 从起点开始
e.col = start_col;
e.f = 0;
e.calculate = abs(end_row - start_row) + abs(end_col - start_col);
e.move_mode = 0;
InsertFirst_L(L, e);
while(FALSE == ListEmpty_L(L)) { // 队列非空执行循环
ListDelete(L, 1, 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.calculate = abs(end_row - temp.row) + abs(end_col - temp.col);
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;
InsertNode_L(L, temp); // 将子结点加入链表
} else if(map[temp.row][temp.col] == -3) {
result[temp.row][temp.col] = temp.move_mode;
InsertNode_L(L, temp); // 将子结点加入链表
}
}
// 往右扩展结点
if(e.col < m-1) {
temp.row = e.row;
temp.col = e.col + 1;
temp.f = e.f + 1;
temp.calculate = abs(end_row - temp.row) +abs(end_col - temp.col);
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;
InsertNode_L(L, temp); // 将子结点加入链表
} else if(map[temp.row][temp.col] == -3) {
result[temp.row][temp.col] = temp.move_mode;
InsertNode_L(L, temp); // 将子结点加入链表
}
}
// 往下扩展结点
if(e.row < n-1) {
temp.row = e.row + 1;
temp.col = e.col;
temp.f = e.f + 1;
temp.calculate = abs(end_row - temp.row) +abs(end_col - temp.col);
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;
InsertNode_L(L, temp); // 将子结点加入链表
} else if(map[temp.row][temp.col] == -3) {
result[temp.row][temp.col] = temp.move_mode;
InsertNode_L(L, temp); // 将子结点加入链表
}
}
// 往左扩展结点
if(e.col > 0) {
temp.row = e.row;
temp.col = e.col - 1;
temp.f = e.f + 1;
temp.calculate = abs(end_row - temp.row) +abs(end_col - temp.col);
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;
InsertNode_L(L, temp); // 将子结点加入链表
} else if(map[temp.row][temp.col] == -3) {
result[temp.row][temp.col] = temp.move_mode;
InsertNode_L(L, temp); // 将子结点加入链表
}
}
}
if((true == ListEmpty_L(L))&&( -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];
}
}
DestroyList_L(L);
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;
}
程序运行结果截图