迷宫问题
一,无权值迷宫
如下图所示的一个迷宫,
其中1表示障碍,求从左上角出发到各个点的最短距离
这时很容易想到的就是广度优先搜索
可以借助队列实现
首先(0,0)入队列
为了方便理解,下面会给出两个图,左图为最小步数,右图为队列的顺序
当队列不为空时,执行以下操作
获得队首(0,0)并出队列,并把临近的点(1,0)进队列,并得出到该点的步数,如图:
接着获得队首(1,0)并出队列,把临近的点(2,0)进队列,如图:
接下来很多是相同的,
上面的处理基本上是一次只进一个到队列的,依照上面的走法到下面这一步:
接下来轮到处理第13个出队列的,同样是获得临近的点,假设获取顺序为上下左右(顺序不影响最小步数),则此时(0,4),(2,4),(1,5)依次进入队列中,如图:
接下来同样获得队首(0,4)并出队首,将其临近的点(0,5)进队列,
到最后可以得到:
由于迷宫没有权值,所以先出队列的点都是步数最小的点。假设出队列的点不是步数最小的点,那么则存在步数更小的点在队列中,但根据队列的特点:先进先出,每次进去一个点,该点是当前相邻的步数+1,也就是说后面进队列的点要么比前面进队列的步数大1,要么就是同样是该点临近的点,也就是与该点步数相同,即后面的点的步数大于等于前面的点,这就与假设矛盾了
时间复杂度为O(nm),因为每个点只进出队列一次
代码如下:
#include <cstdio>
#include <queue>
#define INF 0x3f3f3f
const int n(10), m(10);
int a[n][m] =
{
{0, 0, 0, 1, 0, 0, 1, 1, 1, 0},
{1, 0, 0, 1, 1, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 1, 1, 0, 1, 1, 1},
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0}
};//无权值迷宫
int key[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};//4个方向
int bfsMin[n][m];//最小步数
std::queue<std::pair<int, int> > q;//队列
int main()
{
q.push(std::pair<int, int>(0, 0));//先把(0,0)进队列
for (int i = 0; i < n; ++i){//除不能走的点外初始化为无穷大
for (int j = 0; j < m; ++j){
bfsMin[i][j] = a[i][j] == 0 ? INF : -1;
}
}
bfsMin[0][0] = 0;//左上角的点不用走
std::pair<int, int> front;
int x, y;
while (!q.empty()){
front = q.front();//获得队首
for (int i = 0; i < 4; ++i){//分别获得上下左右四个点的坐标
x = front.first + key[i][0];
y = front.second + key[i][1];
if (x >= 0 && x < n && y >= 0 && y < m && bfsMin[x][y] == INF){//如果该点可走且没走过
q.push(std::pair<int, int>(x, y));//进队列
bfsMin[x][y] = bfsMin[front.first][front.second] + 1;
}
}
q.pop();//队首出队列
}
for (int i = 0; i < n; ++i){
for (int j = 0; j < m; ++j){
printf("%2d ", bfsMin[i][j]);
}
putchar('\n');
}
return 0;
} 运行结果:
###二,带权值迷宫
如图:
其实思想和上面的无权值迷宫相同,只是把队列改成优先队列,每次出队列再赋值,
时间复杂度O(nmlognm),每个点最多进出优先队列4次
代码如下:
#include <cstdio>
#include <queue>
#define INF 0x3f3f3f
const int n(4), m(6);
int a[n][m] =
{
{ 0, 1, 3, 1, 4, 7},
{ 2, -1, 4, 2, -1, -1},
{ 1, 5, -1, 5, 1, 2},
{ 3, 2, 2, 3, 6, 3}
};//带权值迷宫
int min[n][m];//最小步数
int key[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};//4个方向
struct Data
{
int x, y, d;//(x,y)为坐标,d为值
Data(int, int, int);
bool operator>(const Data&) const;//使用优先队列需要重载
};
std::priority_queue<Data, std::vector<Data>, std::greater<Data> > q;//优先队列
Data::Data(int i, int j, int data) : x(i), y(j), d(data){}
bool Data::operator>(const Data &data) const
{
return d > data.d;
}
int main()
{
for (int i = 0; i < n; ++i){//除不能走的点外,初始化为无穷大
for (int j = 0; j < m; ++j){
min[i][j] = a[i][j] != -1 ? INF : -1;
}
}
q.push(Data(0, 0, 0));//左上角进队列
int x, y;
while (!q.empty()){
Data d = q.top();//获得队首
if (min[d.x][d.y] == INF){//如果该点还没走过
min[d.x][d.y] = d.d;
for (int i = 0; i < 4; ++i){//获得4个方向的坐标
x = d.x + key[i][0];
y = d.y + key[i][1];
if (x >= 0 && x < n && y >= 0 && y < m && min[x][y] == INF){//如果该点可以走且没走过
q.push(Data(x, y, d.d + a[x][y]));
}
}
}
q.pop();//队首出队列
}
for (int i = 0; i < n; ++i){
for (int j = 0; j < m; ++j){
printf("%2d ", min[i][j]);
}
putchar('\n');
}
return 0;
}运行结果:

查看3道真题和解析