迷宫问题

一,无权值迷宫

如下图所示的一个迷宫,
无权值迷宫
其中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;
}

运行结果:
运行结果

全部评论

相关推荐

像好涩一样好学:这公司我也拿过 基本明确周六加班 工资还凑活 另外下次镜头往上点儿
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务