佈線問題分支界限法求解

問題描述

印刷電路板將佈線區域劃分成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;
}

運行結果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章