C++算法——BFS

BFS我们称之为宽搜,通常可以用于解决最短,最小问题。不同于深搜,宽搜每次先把同一层的遍历一遍,若无正确答案再去遍历下一层,因此不需要用到递归,只需要用到循环即可。

先来看一道经典例题:走迷宫

在这里插入图片描述
在这里插入图片描述

解决走迷宫问题,我们可以用程序来模拟一下走迷宫,但不同人走迷宫,我们通过程序可以在一个位置从上下左右去尝试,直到找到正确答案。

在这里插入图片描述

解决走迷宫问题,我们可以用程序来模拟一下走迷宫,但不同人走迷宫,我们通过程序可以在一个位置从上下左右去尝试,直到找到正确答案。但前提是向该方向走一步后,到达的点是否合法,因此要进行判断。同时如果该点已经走过,那我们也不要再走一遍。

#include<bits/stdc++.h>

using namespace std;
typedef pair<int , int> PII;

const int N = 110;
//g数组用来存储地图,d数组用来每一个点到起始点的距离
int g[N][N] , d[N][N];
int n , m;
//队列,表示当前走到的点
PII q[N * N];

int bfs()
{
    //定义队头队尾
    int hh = 0 , tt = 0;
    //先把第一个点放入当前走的点
    q[0] = {0 , 0};

    //初始化d数组,使每个点都没涉足
    memset(d , -1 , sizeof d);

    //表示第0个点已经走过了
    d[0][0] = 0;

    //定义了每次的x和y的偏移量
    int dx[4] = {-1 , 0 , 1 , 0};
    int dy[4] = {0 , 1 , 0 , -1};

    //
    while( hh <= tt )
    {
        //取队头元素
        PII t = q[hh ++ ];

        for(int i = 0 ; i < 4 ; i++)
        {
            int x = t.first + dx[i];
            int y = t.second + dy[i];
            //判断该点不超出范围,可走并且没走过
            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;
                //新点入列
                q[ ++tt ] = {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;
}

作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/582768/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

宽搜同样总结起来就是:

(1)处理好已经遍历的点

(2)注意同层之间的数据转换关系

(3)一定要还原现场!

接下来来看一道难题

在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;

int bfs( string start )
{
    string end = "12345678x";

    queue<string> q;

    unordered_map< string , int > d;

    q.push(start);

    //startd的初始距离为0
    d[start] = 0;

    int dx[4] = {1 , 0 , -1 , 0};
    int dy[4] = {0 , 1 , 0 , -1};

    //若队列未空,继续搜索
    while( q.size() )
    {
        //取队列的头元素
        auto t = q.front();

        q.pop();

        if( t == end ) return d[t];

        //取出当前状态的距离
        int dis = d[t];

        //找出x的位置
        int k = t.find( 'x' );

        //将x的位置转化为二维数组的下标
        int x = k / 3 , y = k % 3;

        for(int i = 0 ; i < 4 ; i++)
        {
            for(int j = 0 ; j < 4 ; j++)
            {
                //将x往四个方向移动
                int a = x + dx[i] , b = y + dy[i];

                //如果移动后的点没有越界
                if( a >= 0 && a < 3 && b >= 0 && b < 3 )
                {
                    swap( t[k] , t[ a * 3 + b ] );

                    //如果当前状态没有被记录
                    if( !d.count(t) )
                    {
                        //当前状态为上一上一状态的距离+1
                        d[t] = dis + 1;
                        //入队
                        q.push(t);
                    }
                    //因为还要判断下一次移动的状态,所以要还原现场
                    swap( t[k] , t[ a * 3 + b ] );
                }
            }
        }
    }

    return -1;
}

int main()
{
    string start;

    for(int i = 0 ; i < 9 ; i++)
    {
        char x;
        cin >> x;
        start += x;
    }

    cout << bfs(start) << endl;

    return 0;
}

作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/585447/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值