基于链队数据结构+队列式(FIFO)分支限界算法的布线问题的求解(C语言版)

布线问题

题目描述印刷电路板将布线区域划分成 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;
}
 

运行结果

猜你喜欢

转载自blog.csdn.net/tangguofeng/article/details/142978780