AcWing 844. 走迷宫(bfs)

题目链接 :点击查看

题目描述 :

给定一个 n×m 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0 表示可以走的路,1 表示不可通过的墙壁。

最初,有一个人位于左上角 (1,1)处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。

请问,该人从左上角移动至右下角 (n,m) 处,至少需要移动多少次。

数据保证 (1,1) 处和 (n,m) 处的数字为 0 ,且一定至少存在一条通路。

输入输出格式 :

输入

第一行包含两个整数 n 和 m。

接下来 n 行,每行包含 m 个整数(0 或 1),表示完整的二维数组迷宫。

输出

输出一个整数,表示从左上角移动至右下角的最少移动次数。

输入输出样例 :

输入

5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

输出

8

题目分析 :

本题是求从图左上角移动至右下角的最少移动次数,且题目保证一定至少存在一条通路,可以抽象成每段路权重都相同的最短路问题,我们可以用bfs来做,但不可以用dfs来做,因为dfs不能保证搜到的路径最短,但是是可以搜索到路径的。实现bfs的核心是队列,对于本题来说,由于是对点进行操作,因此我们将queue定义成pair 类型,初始时我们要先将(0, 0)点入队,(0, 0)点即为队头元素,表明当前要进行搜索是从(0, 0)点开始出发,总的来说,队头元素即为每一次搜索的开始位置。而且在搜索之前我们要定义一个d[x][y]数组,表示从(0, 0)点到(x, y)点所需要移动的步数,初始化为-1,-1这个值可以起到表标识作用,如果d[x][y] == -1,表明(x, y)之前没有被搜索到,可以纳入当前搜索(当然还要考虑其他条件),反之则不能,特别地,d[0][0] 要初始化为 0。然后我们开始进行搜索,广搜可以在while循环中进行,队列的大小作为搜索是否结束的标志,如果q.size() == 0表明搜素结束。while中每一次循环都相当于一次搜索,要将队头元素取出,作为本次搜索的起始点。

但搜索具体要怎么去做呢,广度优先搜索的核心行为是什么?我们知道每个点只能朝4个方向进行移动(不考虑边界点,这里是普遍情况),每一次搜索我们将当前起始点向4个方向移动,判断周围满足条件的点,并将其入队,表明向这个点可以走。移动过程我们可以借助方向数组dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}在for循环中完成从起始点向4个方向的移动。判断条件要考虑是否越界,当前点是否是通路,当前点是否被搜索过这三种情况,要是这三种情况都满足,则对记录距离的数组d进行更新,并将此点入队。总的来说,广度优先搜索的行为核心是从当前点出发对周围点进行试探,试探出满足的点将其入队。在从那个满足条件的点对周围点进行试探,如此循环往复……直到队列内点全部出队。详见如下代码。

代码 :

#include
#include
#include
#include
#include
using namespace std;
typedef pair PII;
const int N = 1e2 + 7;
int d[N][N], g[N][N];//d[x][y]是表示从(0, 0)到(x, y)需要移动的步数 
queue  q;//队列,储存的是点,为pair类型 
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};//表示移动的方向,顺序为左、上、右、下 
int main()  {
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < n; i ++ ) 
	  for (int j = 0; j < m; j ++ ) 
	    cin >> g[i][j];//初始化图阵 
	memset(d, -1, sizeof(d));//初始距离均为-1除了(0, 0)点 同时也标明此点有没有被用过 
	q.push({0, 0});//先将(0, 0)点压入队列中 
	d[0][0] = 0;
	while (q.size()) {
	   auto t = q.front();
	   q.pop();//取出队头元素 在此可以保证g[t.first][t.second]一定为0, 因为除了(0,0)点,其他点都会经过判断才会入队列 
	   for (int i = 0; i < 4; i ++ ) {
	   	   int x = t.first + dx[i], y = t.second + dy[i]; //移动 
	   	   if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1) {//判断 
	   	       d[x][y] = d[t.first][t.second] + 1;//更新距离数组 
			   q.push({x, y}); //入队      	
		   }
	   }
	}
	cout << d[n - 1][m - 1] << endl;//输出(n - 1,m - 1)点到(0, 0)点距离 
	return 0;  
}

-------------------------------------------------------------------------------------------

在此我们给出bfs相关模板


intdir[2][4] = { { 1, 0,-1, 0 }, { 0, 1, 0, -1 } };
status tmp1, tmp2;
tmp1.x = x, tmp1.y = y, tmp1.t = 0;
boolflag = false;
q.push(tmp1);                      ///< 根节点入队
while(!q.empty())                  ///< 检查队列是否为空
{
    tmp1 = q.front();              ///< 获取队首元素
    q.pop();                       ///< 队首元素出队
    for(inti = 0; i < 4; i++)     ///< 得到队首元素的四个方向状态
    {
        tmp2 = tmp1, tmp2.x += dir[0][i], tmp2.y += dir[1][i], tmp2.t++;
        if(!valid(tmp2)) continue; ///< 若这个坐标不合法
        if(is_end(tmp2))           ///< 若坐标是终坐标则输出,并且标记已得到答案
        {
            flag = true;
            output(tmp2);
            break;
 
        }
        else q.push(tmp2);         ///< 新坐标状态入队
    }
 
    if(flag) break;
}

 

 

你可能感兴趣的:(早年算法竞赛学过的知识点,队列,bfs)