给定一个n*m大小的迷宫,其中*代表不可通过的墙壁,而‘.代表平地,S表示起点,T表示终点。移动过程中,如果当前位置是(x,y)(下标从0开始),且每次只能前往上下左右四个位置的平地,求从起点S到达终点T的最少步数。
样例:
.....
.*.*.
.*S*.
.***.
...T*
在上面样例中,S的坐标为(2,2),T的坐标为(4,3)。
在本题中,由于求的是最少步数,BFS是通过层次的顺序来遍历的,因此可以从起点S开始计数遍历的层数,那么在到达终点T时的层数就是需要求解的起点S到达终点T的最少步数。
#include<iostream>
#include<queue>
using namespace std;
struct node
{
int x;
int y;//位置x,y
int step;//step为起点s到此位置所需步数
}S, T, p;//p为临时节点
int m, n;
char s[100][100];
bool inq[100][100] = { false };
int d[4][2] = { 0,1,0,-1,1,0,-1,0 };
bool test(node t)//检验此节点是否有效
{
if (t.x<0 || t.y<0 || t.x >= m || t.y >= n || s[t.x][t.y] == '*' || inq[t.x][t.y] == true)return false;//越界,墙壁,已经入过队
return true;//有效位置
}
int bfs()
{
int i;
queue<node> t;//定义队列
node top;//记录队首元素
t.push(S);//起点入队
inq[S.x][S.y] = true;
while (!t.empty())
{
top = t.front();//取出队首元素
t.pop();//队首元素出队
if (s[top.x][top.y] == 'T')return top.step;//终点,返回最少步数
for (i = 0; i<4; i++)//四个方向
{
p.x = top.x + d[i][0];
p.y = top.y + d[i][1];
if (test(p))//如果位置有效
{
p.step = top.step + 1;//相对于上一层的元素(队首),步数加1
t.push(p);
inq[p.x][p.y] = true;//设置为已入过队
}
}
}
return -1;
}
int main()
{
cin >> m >> n;
getchar();
for (int i = 0; i<m; i++)
gets(s[i]);
cin >> S.x >> S.y >> T.x >> T.y;//起点和终点坐标
S.step = 0;//初始化层数为0
cout << bfs();
}
inq数组的含义是判断节点是否已经入过队,而不是节点是否已被访问过(保证了最短路径),区别在于:如果设置成是否已被访问过,有可能在某个节点正在队列中(但还未访问)时由于其他节点可以到达它而将这个节点再次入队,导致很多节点反复入队,计算量大大增加。因此BFS中让每个节点只入队一次,故需要设置inq数组的含义为节点是否已经入过队而非节点是否已被访问。
当STL用queue时,元素入队的push操作只是制造了该元素的一个副本入队,因此入队后对该元素修改不会影响队列中的副本,而对队列中副本的修改也不会改变原元素;