广度优先搜索BFS:迷宫最短路径问题

#include <queue>
#include <memory>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;

const int MAX_SIZE = 25;
const int INF_INT = 0x3f3f3f3f;

//用点来走迷宫,有面向对象那味,比较好写。如果单纯记录坐标,就很不直观,变量会很多。
struct Pos
{
	int x, y;
	//关系运算符重载,判等
	bool operator == (const Pos& rhs) const
	{
		return x == rhs.x && y == rhs.y;
	}
	bool operator != (const Pos& rhs) const
	{
		return !(*this == rhs);
	}
};

//判断是否跑出边界
bool inside_judge(const Pos& pos, int n, int m)
{
	return 0 <= pos.x && pos.x < n && 0 <= pos.y && pos.y < m;
}

int bfs(const Pos& src, const Pos& sink, int n, int m, int maze[][MAX_SIZE])
{
	const int dir[4][2] = { {-1, 0}, {0, -1}, {1, 0}, {0, 1} };     //定义四个方向  也可以写两个一维数组,只要能实现改变横纵坐标就行
	int dp[MAX_SIZE][MAX_SIZE];          //保存最短路径,depth。应该也可以直接在Point类里面定义一个depth变量保存该点到起点的最短路径
	queue<Pos> que;            //广度优先的那个队列,起始点入栈
	que.push(src);
	memset(dp, INF_INT, sizeof(dp));           //把数组中的数值初始化成正无穷大
	dp[src.x][src.y] = 0;            //起点路径=0
	while (!que.empty())
	{
		Pos now = que.front();
		que.pop();
		if (now == sink)              //重载的==
		{
			return dp[sink.x][sink.y];     //即最短路径
		}
		//没到终点就继续搜索,广度优先,4次循环,四个方向都走一格,每走一格都计算一下附近的最短路径。而不是往一个方向钻。
		//只要能走,就给他走一下
		for (int ind = 0; ind < 4; ++ind)
		{
			Pos next = now;
			next.x += dir[ind][0];          //设定下一个的坐标,有向上下左右4种可能,都改变一下坐标看看有没有越界、是不是0
			next.y += dir[ind][1];          //dir[4][2] = { {-1, 0}, {0, -1}, {1, 0}, {0, 1} }; 
											//dir[4][2]的顺序为【左上右下】,所以一开始往下走,dp[0][-1]=1,然后(0,-1)这个点入队列
											//然后回来,再初始化next结点为now,再往右走,dp[1][0]=1,然后(1,0)这个点入队列。
											//然后这个for循环就结束了,回到while循环,判断队列不空,拿到队首元素(0,-1),继续上述操作
			/* 2 0 0
			   0 0 1
			   3 0 1*/       //这就是maze的本尊
			if (inside_judge(next, n, m) && 0 == maze[next.x][next.y])      //如果没有出边界,而且maze等于0,就可以走。
			{                                       //注意maze的值没有改动,所以他来到中间的时候,可以往上走也可以往下走,但是中间往上走,在下面判定出路径比原来的大,就不会往上走。然而事实上,按这张图来讲,直接下去两格就结束了,now不到中间的结点
				//PPT的定理1。如果next这个点的距离在前面已求得,就让now的路径+1跟他比一比,如果比他小,就更新next的最短距离。
				
				//感觉如果不用这个判断,直接搞个数组来判断是否走过了,走过了就不走也行。因为不重复的BFS搜索到的终点一定是最短距离
				if (dp[now.x][now.y] + 1 < dp[next.x][next.y])    //如果next的距离还没求得,那就是初始值ox3f3f3f3f      
				{
					//更新距离和入队同时进行,也就是只有找到了更短的距离,才会有结点入队。
					//因为如果每次能走通的结点都入队,就会出现重复入队
					dp[next.x][next.y] = dp[now.x][now.y] + 1;
					que.push(next);       //塞入队列,就跟ppt里面那个队列的图一样,能塞几个塞几个(通过for循环,一次最多塞一个)
				}
			}
		}
	}
	return -1;
}

void incubate()
{
	int n, m;
	int maze[MAX_SIZE][MAX_SIZE];
	/* 2 0 0
	   1 0 1
	   3 0 1*/         //maze填入数据后,就长这样
	Pos src, sink;                     //起点和终点
	int ans;
	while (cin >> n >> m)              //这是什么骚操作?
	{
		for (int i = 0; i < n; ++i)
		{
			for (int j = 0; j < m; ++j)
			{
				cin >> maze[i][j];
			}
		}
		for (int i = 0; i < n; ++i)
		{
			for (int j = 0; j < m; ++j)
			{
				switch (maze[i][j])         //寻找起始和终点
				{
				case 2: src.x = i, src.y = j, maze[i][j] = 1;
					break;

				case 3: sink.x = i, sink.y = j, maze[i][j] = 0;
					break;

				default:
					break;
				}
			}
		}
		ans = bfs(src, sink, n, m, maze);
		if (-1 == ans)
		{
			cout << "Disconnected!" << endl;
		}
		else
		{
			cout << "The miminum distance from start point to target point is"
				<< ans << endl;
		}
	}
	return;
}
int main(int argc, char* argv[])
{
	incubate();
	return 0;
}

/*
测试数据:
connected
3 3
2 0 0
1 0 1
3 0 1
connected
3 3
2 0 0
0 0 1
3 0 1
disconnected
3 3
2 0 0
1 1 1
3 0 1
 */

======================分割线=帮助理解===============================

  • 进入队列的过程

    • 入队和计算路程是一起搞的

  • 举个例子,能更好的理解dfs函数里面的最后一个判断语句

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值