#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函数里面的最后一个判断语句