P1746

这是一道BFS(广度优先搜索)的模版题

我们先明确任务是什么

给出一个方型地图,标出每一个点是否可以通过。同时给出起点和终点,在只能垂直或水平地前进的条件下,求出最短路径。


动手模拟

这有点像小时候玩的走迷宫,但是又有点不同,路径不唯一,并且要求求出最短路的长。通过给出的样例,我们可以很轻松地找出一条最短路(同时也是唯一的路径)。

但要是扩展开来,我们将面对一个随机的地图,该如何保证能找到最短路呢?


枚举

这是最笨的办法,但有时候也最管用。

我们在走迷宫时常常会这么做,一步一步试,摸着石头过河。每当我们走到一个错误的点(死路,或者超出地图范围),我们就会记下来,然后换一条路走,重复上述过程,直到走到终点。


如何用计算机语言实现?

在中学课本中我们学过,就是把自然语言转换为计算机语言,而上面讲的过程就是自然语言。

我们不妨用一个数组将地图存下来。从起点开始,上下左右走,到一个新的点,重复上述过程,直至走到终点。

如何实现走到一个新的点,继续往下走呢?我们需要用到一个数据结构,队列。

简单地说,队列是这样的。

队列是一种特殊的线性表,
特殊之处在于:它只允许在表的前端进行删除操作,只允许在表的后端进行插入操作;

队列是一种操作受限制的线性表;

进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

队列的数据元素又称为队列元素。

在队列中插入一个队列元素称为入队,

从队列中删除一个队列元素称为出队。

因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。

/摘自 百度/
顺序队列

现在我们可以将一个点的横纵坐标作为一个元素,放进队列中。(初始放入起点)然后我们将队头的元素弹出,并把它上下左右的点试着放入队列中,然后继续上述操作,直到找到终点。这一个过程就叫 广度优先搜索


搜索的边界

聪明的你应该发现了,并不是所有的点都能被放入队列中,因此我们需要考虑搜索的边界。比如走到超出地图边界的点不能放入,已经走过的点不能放入,不能通过的点也不能放入。


正确性

我们说到达终点,搜索就停止了,那么我们怎么证明,一旦到达终点,就是最短的那一条路呢。

这就是BFS的特点了,我们注意观察队列的特性还有我们放入点的顺序,搜索的过程符合下图。

bfs

我们发现,我们每次都在记录第几步能搜素到达哪些点,那么最先到达终点的那一次,步数就是最短路的距离


好了,现在我们排除万难,终于能让这个伟大的算法跑起来了

附上coding

#include<bits/stdc++.h>
using namespace std;
int n;
int vis[1005][1005];
int h[4]={0,0,1,-1},s[4]={1,-1,0,0};
//h、s数组代表可以走的上下左右四个方向
char a[1005][1005];
//a数组表示地图
struct node{
	int x,y;
};
//结构体
queue<node>q;
//如上,队列q
bool check(int x,int y){	
	if(a[x][y]=='1')
	    return false;
    	//如果是障碍物,就返回false
	if(vis[x][y]>0)
	    return false;
   	//如果以前走过了,就返回false
	if(x>n||x<1)
	    return false;
	if(y>n||y<1)
	    return false;
        //如果超出地图边界,就返回false
	return true;
    	//如果它通过了重重考验,就给它过吧
}
void bfs(int x,int y){
	vis[x][y]=1;
    	//标记起点
	q.push((node){x,y});
    	//把起点和终点放进队列q里 
	while(q.size()!=0){
    	    //如果队列里还有数,就继续
	    int xx=q.front().x;
	    int yy=q.front().y;
            //把队列里的数取出来
	    q.pop();
            //用过了就把它扔了
	    for(int i=0;i<4;i++){
            //代表四个方向
		int xxx=xx+h[i];
		int yyy=yy+s[i];
                //向某一个方向前进
		if(check(xxx,yyy)){
		    vis[xxx][yyy]=vis[xx][yy]+1;
            	    //记录步数
		    q.push((node){xxx,yyy});
            	    //把通过的答案扔进队列里
	        }
	    }
        }
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
	    for(int j=1;j<=n;j++){
		cin>>a[i][j];
	    }
	}
    	//地图
	int x1,x2,y1,y2;
    	//x1表示起点的横坐标,x2表示起点的纵坐标
	scanf("%d%d%d%d",&x1,&x2,&y1,&y2);
	bfs(x1,x2);
	printf("%d",vis[y1][y2]-1);
	//输出答案减一,它把起点也算了一步
	return 0;
} 

代码来自此题第一篇题解

posted @ 2026-08-19 22:38  zxy1007  阅读(13)  评论(1)    收藏  举报