分支限界法:布线问题

问题描述:

  印刷电路板不限区域划分成n*m个方格阵列。如下图所示


  精确的电路布线问题要求确定连接方格a的中点,到连接方格b的中点的最短布线方案。

  布线时,电路只能沿直线或直角布线。为了避免线路相交,已布的线的方格做了封锁标记,其他线路不允许穿过被封锁的方格。

分支限界法的解决方案:

  首先从起始位置a开始,将它作为第一个扩展结点。与该节点相邻,并且可达的方格成为可行结点被加入到活节点队列中,并且将这些方格标记为1.

    即从起始方格a到这些扩展方格距离为1.

  然后从活节点队列中取出队首结点作为下一个扩展结点,并将于当前扩展结点相邻且为未标记过的方格标记为2,并存入或节点队列。

  最后这个过程一直到算法搜索到目标方格b或活结点队列为空时截止。

实现方案:

  初始定义Position,定义变量x,y,显示方格 行 列。

  grid[i][j]表示方格阵列的 0 : 开放, 1 :封锁。

  2个方格相同,则不必计算,直接返回最小距离。

  否则,设置方格围墙,初始化位移矩阵offset。

  表示距离时,0,1已经使用,直接从2开始。因此所有距离最后都要减2. 

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define Maxsize 20
#define N 7
#define M 7

int grid[N+2][M+2];//这个矩阵是N*M的,+2的目的是为了设置边界,每个小方块上面的值,1代表死路,0代表活路,其他数字-2代表到起点的距离

struct Position//定义一个位置结构体,x、y分别为横坐标和纵坐标
{
	int x;
	int y;
};

struct Queue//定义一个队列,方便进行FIFO操作,注意这里队列里面元素的类型是之前定义的Position类型
{
	Position pos[Maxsize];
	int front;
	int rear;
};

void init(struct Queue &Q)//初始化队列
{
	Q.front=Q.rear=0;
}

int isEmpty(struct Queue Q)//判队空
{
	return Q.front==Q.rear;
}

void EnQ(struct Queue &Q,Position x)//入队
{
	if(Q.front!=(Q.rear+1)%Maxsize)
	{
		Q.rear=(Q.rear+1)%Maxsize;
		Q.pos[Q.rear]=x;
	}
}

void DeQ(struct Queue &Q,Position &x)//出队
{
	if(Q.front!=Q.rear)
	{
		Q.front=(Q.front+1)%Maxsize;
		x=Q.pos[Q.front];
	}
}

int FindPath(Position start,Position finish,Position *&path,int &PathLen)//核心函数,start和finish为两个位置,分别代表起始坐标,path是一个指向Position结构体类型的指针,装的是最终走的路径加引用标志是为了改变实参*path,PathLen是指路径的长度,不包含起点但是包含终点
{
	struct Queue Q;//定义一个队列
	Position here,nbr,offset[4];//定义两个位置变量,一个是here指当前位置,nbr为下一个探索的位置,here除了初始值以外,here取啥值全靠cbr来探索,offset[4]为四个动作,为别是右下左上
	init(Q);//初始化队列
	if(start.x==finish.x&&start.y==finish.y)//如果起点就是终点就直接返回1
		return 1;
	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;
	here.x=start.x;//初始化here=start
	here.y=start.y;
	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;
	grid[start.x][start.y]=2;//因为1和0都被用了,所以起点在grid[][]上的初始值为2,这个值-2就是到起点的距离
	do//开始一轮一轮的循环
	{
		for(int i=0;i<4;i++)//四个方位探索
		{
			nbr.x=here.x+offset[i].x;//nbr为下一个探索的值
			nbr.y=here.y+offset[i].y;
			if(grid[nbr.x][nbr.y]==0)//如果下一个探索的位置是活路
            {
				grid[nbr.x][nbr.y]=grid[here.x][here.y]+1;//就让这个小方块上面的数字=here上面的数字+1,表示距离又+1
				if(nbr.x==finish.x&&nbr.y==finish.y)//再判断这个探索的位置是不是终点,如果是就退出while(1)循环
					break;
				EnQ(Q,nbr);//如果不是终点的话就让这个位置入队
			}
		}//四个方向探索完毕
		if(nbr.x==finish.x&&nbr.y==finish.y)//探索完毕之后看看刚才是不是探索到终点了,如果刚才break了,说明就到终点啦,那么就赶快退出循环进行下一步回溯操作吧!
			break;
		if(isEmpty(Q))//如果在四个方向探索之后没有到达终点,并且这个时候队列里空空如也,说明不可能有可行解了,就返回0
			return 0;
		DeQ(Q,here);//如果在四个方向探索之后没有到达终点,并且这个时候队列里还有东西,就把队头取出来给here继续进行while(1)循环
	}while(1);//while(1)结束
	/*如果程序执行到这一步,说明有可行解,不然早返回0啦!下面的工作就是要看看到底是怎么样一条路径!*/
	PathLen=grid[finish.x][finish.y]-2;//这-2是因为grid[][]上面的数字,除了1和0以外,-2之后就是到起点的距离,说明finish-2就是到起点的距离
	path=(Position *)malloc(sizeof(PathLen));//path本来就是一个地址,所以给他开辟PathLen这么长的空间用来存储路径
	here=finish;//通过反向回溯的方法来寻找,就先让here=finish
	for(int j=PathLen-1;j>=0;j--)//开始反向回溯,j是指path[]的下标,下标从0~PathLen-1
	{
		path[j]=here;//最后一个位置当然是终点啦,把这句话放在这里的好处是方便循环,直接让path[j]=探索出来的here
		for(int i=0;i<4;i++)//就要探索咯,四个方位
		{
			nbr.x=here.x+offset[i].x;
			nbr.y=here.y+offset[i].y;
			if(grid[nbr.x][nbr.y]+1==grid[here.x][here.y])//如果这个方位here这个位置的数-1,那么这个数就是我想要的
			{
				here=nbr;//就把这个数给here
				break;//同时结束循环
			}
		}
	}
	return 1;
}

int main()
{
	grid[N+2][M+2]={0};//首先让grid[][]全部是0
	grid[5][1]=grid[6][1]=grid[7][1]=grid[6][2]=grid[7][2]=grid[1][3]=grid[2][3]=grid[6][3]=grid[7][3]=grid[2][4]=grid[4][4]=grid[3][5]=grid[4][5]=grid[5][5]=1;//设置障碍
	Position *path;//定义path指针
	Position start,finish;//定义起始坐标
	int PathLen;//定义路径长度,通过FindPath函数来取值
	start.x=3;//初始化起点
	start.y=2;
	finish.x=4;//初始化终点
	finish.y=6;
	int ret=FindPath(start,finish,path,PathLen);//ret为返回值
	if(!ret)
        cout<<"没有可行解!"<<endl;
	else//不然就是有解
	{
	    for(int i=0;i<=N+1;i++)//输出grid[][]矩阵
            for(int j=0;j<=M+1;j++)
            {
                if(grid[i][j]>=2)//如果位置上的值既不是0也不是1
                    cout<<grid[i][j]-2<<"  ";//就-2,方便理解
                else//不然
                    cout<<grid[i][j]<<"  ";//正常输出就行
                if(j==M+1)//如果一行结束就换行
                    cout<<endl;
            }
            cout<<"("<<start.x<<","<<start.y<<")"<<endl;//输出起始坐标
            for(int i=0;i<PathLen;i++)//输出最短路径
                cout<<"("<<path[i].x<<","<<path[i].y<<")"<<endl;
	}
	return 0;
}


  • 6
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值