在前面的迷宫中,我们使用了深度优先搜索的方法,这里介绍一个新的方法来解决这个问题——广度优先搜索,也称为宽度优先搜索。
这里还是用一个二维数组来存储迷宫,最开始的时候A也是在迷宫(0, 0)处,他可以往下走或往右走。还是采用和深度优先搜索一样的方法,我们先让他往右走,然后一直尝试下去,直到走不通了再返回这里。这样就是深度优先,可以通过函数的递归方式来实现。现在介绍的方法是:通过一层一层的拓展的方法来实现找打B。拓展时发现一个点就将正点加入列表中,直至发现B的位置。
最开始A的位置是在入口(0, 0),一步之内可以到达(0, 1)和(1, 0)。但是B并不在这两个点上,那么A只能通过这两个点继续往下走。比如现在A走到了(0, 1),那么现在他又能到达哪些新的点呢?有(1, 1),然后再看通过(1, 0)又能到达哪些点呢?可以到达(1, 1)和(2, 0),此时你会发现(1, 1)这个点两个路线都能走到。为了放置一个点被多次走到,这里就需要一个数组来标记这个点是否已经被走到过。
此时A两步可以走到的点有(1,1)和(2, 0),可是B还是不在这两个点上,没有别的办法,还是要继续往下尝试,看看通过刚刚这两个点还能到达哪些新的没有走过的点。通过(1, 1)这个点我们可以到达(1, 2)和(2, 1),通过(2, 0)这个点我们可以到达(2, 1)和(3, 0),现在三步可以到达的点就有了(1, 2)和(2, 1)和(3, 0),现在依然是没有B所在的点,那么我们就需要继续重复刚刚的方法,直到我们找到B为止。
回顾一下刚才的算法,可以用一个队列来模拟这个过程。这里我们还是使用一个结构体来实现队列。
struct note
{
int x;
int y;
int step;
};
struct note que[2051];
int head, tail;
int a[51][51] = {0}; // 存储地图
int book[51][51] = {0}; // 用来标记哪些点已经在队列中,防止一个点被重复拓展
// 初始化队列
head = 0;
tail = 0;
// 将A的起始位置加入队列,并标记这个点已经走过
que[tail].x = 0;
que[head].y = 0;
que[tail].step = 0;
tail++;
book[0][0] = 1;
然后从起始点开始往右尝试走一步
tx = que[head].x;
ty = que[head].y + 1;
现在需要判断这一步是否越界:
if (tx < 0 || tx >= n || ty < 0 || ty >= m) {
continue;
}
还需要判断这个点是否是障碍物,或者是已经在路径中:
if (a[tx][ty] == 0 && book[tx][ty] == 0) {
}
如果这个点满足以上的要求条件,那么这个点可以入队,并标记这个点已经走过
book[tx][ty] = 1;
que[tail].x = tx;
que[tail].y = ty;
que[tail].step = que[head].step + 1;
tail++;
接下来还要继续尝试往其他方向走。这里还是规定一个顺序,即按照顺序时针的方向来尝试。然后第二步发现可以到达(1, 0),这样的话这个点也要加入队列,代码实现和刚刚的(0, 1)的操作是一样的。
对(0, 0)拓展完毕后,其实这个点现在就对我们没有用了,此时我们就将(0, 0)出队。出队的操作就很简单,就一句话:
head++;
接下来我们就是需要将刚刚新拓展出来的两个点的基础上再继续往下探索,进行拓展。到目前为止我们已经拓展的点都是从起点出发之后一步可以到达的点。因此还没有找到B所在的位置,还需要继续进行。
为了四个方向拓展,这里还需要一个next数组来:
int next[4][2] = {{0, 1}, // 右
{1, 0}, // 下
{0, -1}, // 左
{-1, 0}}; // 上
完整的代码如下:
#include <stdio.h>
struct node
{
int x;
int y;
int step;
};
int main()
{
struct node que[2501];
int a[51][51] = {0};
int book[51][51] = {0};
int next[4][2] = {{0, 1}, // 右
{1, 0}, // 下
{0, -1}, // 左
{-1, 0}}; // 上
int head, tail;
int i, j, k, n, m, startx, starty, p, q, tx, ty, flag;
scanf("%d %d", &n, &m);
for (i = 0; i< n; i++) {
for (j = 0; j < m; j++) {
scanf("%d", &a[i][j]);
}
}
scanf("%d %d %d %d", &startx, &starty, &p, &q);
// 队列初始化
head = 0;
tail = 0;
// 往队列插入迷宫的入口坐标
que[tail].x = startx;
que[tail].y = starty;
que[tail].step = 0;
tail++;
book[startx][starty] = 1;
flag = 0; // 用来标记是否已经到达终点
// 当队列不为空的时候循环
while (head < tail) {
// 枚举四个方向
for (k = 0; k <= 3; k++) {
// 计算下一个点的坐标
tx = que[head].x + next[k][0];
ty = que[head].y + next[k][1];
// 判断是否越界
if (tx < 0 || tx > n || ty < 0 || ty >m) {
continue;
}
// 判断是否障碍物或者已经在路径中
if (a[tx][ty] == 0 && book[tx][ty] == 0) {
// 标记这个点已经走过
book[tx][ty] = 1;
// 插入这个点到队列中
que[tail].x = tx;
que[tail].y = ty;
que[tail].step = que[head].step + 1;
tail++;
}
// 如果到达了目标点,停止拓展,任务结束,退出循环
if (tx == p && ty == q) {
flag = 1;
break;
}
}
if (flag == 1){
break;
}
head++; // 拓展完一次后,head++拓展后面的点
}
printf("%d\n", que[tail - 1].step); // tail指向的是队尾的后一个位置,所以这里需要-1
return 0;
}
break;
}
head++; // 拓展完一次后,head++拓展后面的点
}
printf("%d\n", que[tail - 1].step); // tail指向的是队尾的后一个位置,所以这里需要-1
return 0;
}