【算法笔记】BFS(走迷宫,八数码)

Acwing yyds


BFS(宽度优先遍历)

与深度优先搜索一条路走的死的方式不同,宽度优先遍历是一层一层来搜,当一层的节点被搜索完成后,才会进入下一层。因此可以利用BFS来找到最短路径(当路径权值为1的时候)。接下来用两个例题展示。

走迷宫

 思路:从起点开始遍历能走到的点,然后再继续找到能走到的点。以上图为例起点是(0,0),遍历到能够走到的点为(1,0)只有一个。然后继续遍历可走的点(2,0)也只有一个。继续往下(2,1)和(3,0)有两个点可以走。这时就要分别遍历这两个点可以走的路径了。后面过程同上,最后看哪条路径先到达终点,输出最短距离。

要注意的是每一个点只能用一次(走到的点会刷新状态),并且权值相同,这也就保证先走到终点的一定是最短路。

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;

const int N = 110;

int n, m;
int g[N][N]; //存放地图
int d[N][N]; //存放点到原点的距离

int bfs()
{
    queue<PII> q; //存放可走节点的位置信息

    memset(d, -1, sizeof d); //初始化距离为-1,即还没走都该点
    d[0][0] = 0; //从起点开始出发
    q.push({0, 0}); //往队列加入起点坐标

    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; //上下左右平移

    while (q.size()) //队列不为空,若为空则代表节点已全部遍历完
    {
        //一次只搜一个节点
        auto t = q.front(); //取出队头元素
        q.pop(); //弹出对头元素

        for (int i = 0; i < 4; i ++ ) //循环四次,对应四个方向的平移
        {
            int x = t.first + dx[i], y = t.second + dy[i]; //(x,y)对应平移后的坐标

            if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1) //判断是否出界,是否为通路,以及该点是否已被使用过。
            {
                d[x][y] = d[t.first][t.second] + 1; //距离+1
                q.push({x, y}); //将(x,y)加入队列,等待下一次宽搜
            }
        }
    }

    return d[n - 1][m - 1]; //返回最短距离
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < m; j ++ )
            cin >> g[i][j];

    cout << bfs() << endl;

    return 0;
}

 八数码

思路:本质上和最短路相同,每一次交换位置的权值都是1,交换过后的方阵就是一个节点。要找到先到达‘1 2 3 4 5 6 7 8 x’(终点)的路径。

难点1:如何表示方阵的变化——用字符串来表示方阵,用find()函数找到'x'的位置记为k。映射到方阵上对应的坐标就是 x = k / 3, y = k % 3。

难点2:如何记录距离——创建一个关联容器(字典),键表示方阵,值表示距离。

#include<iostream>
#include<cstring>
#include<queue>
#include<unordered_map>


using namespace std;

int bfs(string st)
{
    queue<string> q;
    unordered_map<string, int> d; //创建字典(方阵名,距离)
    
    string end = "12345678x"; //录入终点
    q.push(st); //将起点加入队列
    
    d[st] = 0; 
    int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
    
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        
        int distance = d[t]; //取出该方阵到起点的距离
        if( t == end) return d[t]; //判断该方阵是否是终点,若是返回最短距离
        
        int k = t.find('x'); //找到x的位置
        int x = k / 3, y = k % 3; //映射到方阵
        
        for(int i = 0; i < 4; i ++) //遍历(x,y)的四个方向
        {
            int a = x + dx[i]; 
            int b = y + dy[i]; //(a,b)为(x,y)平移后的坐标
            
            if( a >= 0 && a < 3 && b >= 0 && b < 3) //判断是否出界
            {
                swap(t[k], t[ a * 3 + b]); //交换对应位置上的值,产生新的节点(方阵)
                if(!d.count(t)) //若该节点之前没被遍历过,则距离+1
                {
                    d[t] = distance + 1;
                    q.push(t);
                }
            
                swap(t[k], t[ a * 3 + b]); //恢复状态
            }
        }
    }
    return -1; //找不到答案,返回-1
    
}

int main()
{
    char s[2];
    string st;
    for(int i = 0; i < 9 ; i ++)
    {
        cin>>s;
        st += *s;
    }
    cout<<bfs(st);
}

猜你喜欢

转载自blog.csdn.net/Radein/article/details/134678194
今日推荐