C++演算法——BFS

是挺禿然的齊齊哦發表於2020-11-14

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
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

相關文章