P1746
这是一道BFS(广度优先搜索)的模版题
我们先明确任务是什么
给出一个方型地图,标出每一个点是否可以通过。同时给出起点和终点,在只能垂直或水平地前进的条件下,求出最短路径。
动手模拟
这有点像小时候玩的走迷宫,但是又有点不同,路径不唯一,并且要求求出最短路的长。通过给出的样例,我们可以很轻松地找出一条最短路(同时也是唯一的路径)。
但要是扩展开来,我们将面对一个随机的地图,该如何保证能找到最短路呢?
枚举
这是最笨的办法,但有时候也最管用。
我们在走迷宫时常常会这么做,一步一步试,摸着石头过河。每当我们走到一个错误的点(死路,或者超出地图范围),我们就会记下来,然后换一条路走,重复上述过程,直到走到终点。
如何用计算机语言实现?
在中学课本中我们学过,就是把自然语言转换为计算机语言,而上面讲的过程就是自然语言。
我们不妨用一个数组将地图存下来。从起点开始,上下左右走,到一个新的点,重复上述过程,直至走到终点。
如何实现走到一个新的点,继续往下走呢?我们需要用到一个数据结构,队列。
简单地说,队列是这样的。
队列是一种特殊的线性表,
特殊之处在于:它只允许在表的前端进行删除操作,只允许在表的后端进行插入操作;
队列是一种操作受限制的线性表;
进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。
在队列中插入一个队列元素称为入队,
从队列中删除一个队列元素称为出队。
因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
/摘自 百度/

现在我们可以将一个点的横纵坐标作为一个元素,放进队列中。(初始放入起点)然后我们将队头的元素弹出,并把它上下左右的点试着放入队列中,然后继续上述操作,直到找到终点。这一个过程就叫 广度优先搜索 。
搜索的边界
聪明的你应该发现了,并不是所有的点都能被放入队列中,因此我们需要考虑搜索的边界。比如走到超出地图边界的点不能放入,已经走过的点不能放入,不能通过的点也不能放入。
正确性
我们说到达终点,搜索就停止了,那么我们怎么证明,一旦到达终点,就是最短的那一条路呢。
这就是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;
}
代码来自此题第一篇题解
逃

浙公网安备 33010602011771号