寬度優先搜索(廣度優先搜索&&BFS)

BFS

  • BFS概要
    BFS是一項基本的暴力搜索技術,常用於解決圖和樹的遍歷問題。BFS類似逐層遍歷,其實現依託隊列。可以應用在走迷宮、尋找最短路徑等問題上。

  • 注意點
    1.標記。搜索的時候要及時標記,避免重複訪問。
    2.剪枝。搜索的時候判斷搜索方向是否合理,不能達到目的的搜索方向及時終止。

  • 擴展
    康託展開
    A*算法—搜索+貪心(策略)

    //bool vis[LEN+5];
    //long int fac[10]={1,1,2,6,24,120,720,5040,40320,362880};
    //八數碼問題中的康託展開判重
    bool Cantor(int str[], int n) //未訪問返回true,並標記
    {
        long result=0;
        for(int i=0; i<n; i++)
        {
            int counted=0;
            for(int j=i+1; j<n; j++)
            {
                if(str[i]>str[j]) counted++;
            }
            result += counted*fac[n-i-1];
        }
        if(!vis[result])
        {
            vis[result] = true;
            v++;
            return true;
        }
        return false;
    }
    
  • 練習
    Red and Black
    Catch That Cow
    Find The Multiple
    Prime Path
    Pots
    Lake Counting

    大意:一個人站在黑色的瓷磚上。從瓷磚中,他可以移動到四個相鄰瓷磚中的一個。但他不能在紅瓦上移動,他只能在黑色瓷磚上移動。編寫程序,通過重複上述動作來計算他可以達到的黑色瓷磚的數量。BFS和DFS都可行,主要是對訪問過的進行標記。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef struct
    {
        int x, y;
    }node;
    
    int m, n;
    int d[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
    queue<node> q;
    char r[25][25];
    
    int main()
    {
        while(1)
        {
            cin>> n>> m;
            if(m==0 && n==0) break;
            node p;
            int sum=0;
            for(int i=0; i<m; i++)
            {
                for(int j=0; j<n; j++)
                {
                    cin>> r[i][j];
                    if(r[i][j] == '@')
                    {
                        p.x = j;
                        p.y = i;
                    }
                }
            }
    
            q.push(p);
            r[p.y][p.x] = '#';
    
            while(!q.empty())
            {
                p = q.front();
                q.pop();
                sum++;
                for(int i=0; i<4; i++)
                {
                    node temp=p;
                    temp.x += d[i][0]; temp.y += d[i][1];
    
                    if(temp.x<n && temp.x>=0 && temp.y>=0 && temp.y<m && r[temp.y][temp.x] != '#')
                    {
                        q.push(temp);
                        r[temp.y][temp.x] = '#';
                    }
                }
            }
    
            cout<< sum<< endl;
        }
        return 0;
    }
    
    

    大意:農夫知道一頭牛的位置,想要抓住它。農夫和牛都於數軸上 ,農夫起始位於點 N(0<=N<=100000) ,牛位於點 K(0<=K<=100000) 。農夫有兩種移動方式: 1、從 X移動到 X-1或X+1 ,每次移動花費一分鐘 2、從 X移動到 2*X ,每次移動花費一分鐘 假設牛沒有意識到農夫的行動,站在原地不。最少要花多少時間才能抓住牛?
    搜索時對搜索過的位置進行標記,對於出界的位置不加入隊列,數組在預定義的時候比需要的多一些,不然會RE

    #include<iostream>
    #include<queue>
    #include<string.h>
    using namespace std;
    
    typedef struct
    {
        int N, T;
    }node;
    
    int N, K;
    bool vis[1000000];
    
    void BFS(int N, int K)
    {
        queue<node> q;
        node n={N, 0};
    
        if(n.N == K)
        {
            cout<< "0";
            return ;
        }
    
        q.push(n);
        vis[n.N] = true;
    
        while(!q.empty())
        {
            node m;
            n=q.front();
            q.pop();
    
    
            m.N = n.N+1;
            if(vis[m.N] == false && m.N<=100000 && m.N>=0)
            {
                m.T = n.T+1;
    
                if(m.N == K)
                {
                    cout<< m.T;
                    return ;
                }
    
                vis[m.N] = true;
                q.push(m);
            }
    
    
            m.N = n.N-1;
            if(vis[m.N] == false && m.N<=100000 && m.N>=0)
            {
                m.T = n.T+1;
    
                if(m.N == K)
                {
                    cout<< m.T;
                    return ;
                }
    
                vis[m.N] = true;
                q.push(m);
            }
    
    
            m.N = n.N*2;
            if(vis[m.N] == false && m.N<=100000 && m.N>=0)
            {
                m.T = n.T+1;
    
                if(m.N == K)
                {
                    cout<< m.T;
                    return ;
                }
    
                vis[m.N] = true;
                q.push(m);
            }
    
        }
    }
    
    int main()
    {
        cin>> N>> K;
    
        memset(vis, false, sizeof(vis));
        BFS(N, K);
    
        return 0;
    }
    

    8發+之後終於抓到了這頭牛 (最難抓的一頭牛可能被我遇到了) ,數組莫名的開小了,所以,,,以後還是儘可能多開一點。

    大意就是找一個只由1、0組成的能被給定的數字整除的數。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int n;
    bool solved;
    void DFS(unsigned long long m,int k){
        if(solved) return ;
    
        if(m%n == 0){
            printf("%llu\n", m);
            solved = true;
            return ;
        }
    
        if(k == 19) return;
    
        DFS(m*10, k+1);
        DFS(m*10+1, k+1);
    }
    int main(){
    
        while(cin>> n)
        {
            if(n == 0) break;
            solved = false;
            DFS(1,0);
        }
    
        return 0;
    }
    

    大意: 將一個素數變成另一個素數,一次只能變一位數,並且轉變的過程中也要是素數。輸出最少的次數。先針對給出的數據範圍對素數進行打表,避免超時。

    #include<iostream>
    #include<cmath>
    #include<queue>
    #include<string.h>
    using namespace std;
    
    typedef struct
    {
        int a, p;
    }node;
    
    bool check[100000];
    bool vis[100000];
    int n;
    
    bool Prime(int m)
    {
        for(int i=2; i<=sqrt(m); i++)
        {
            if(m%i==0) return false;
        }
        return true;
    }
    
    void BFS(int a, int b)
    {
        memset(vis, false, sizeof(vis));
    
        node x, y;
        queue<node> q;
    
        x.a = a;
        x.p = 0;
        q.push(x);
        vis[x.a] = true;
    
        while(!q.empty())
        {
            x = q.front();
            q.pop();
    
            int t[4];
            t[0] = x.a/1000;//千
            t[1] = (x.a/100)%10;//百
            t[2] = (x.a/10)%10;//十
            t[3] = x.a%10;//個
    
            for(int i=0; i<4; i++)
            {
                int temp = t[i];
                for(int j=0; j<10; j++)
                {
                    if(j == t[i]) continue;
                    t[i] = j;
                    y.a = t[0]*1000+t[1]*100+t[2]*10+t[3];
    
                    if(y.a == b)
                    {
                        cout<< x.p+1<< endl;
                        return ;
                    }
    
                    if(check[y.a] && !vis[y.a])
                    {
                        vis[y.a] = true;
    
                        y.p = x.p+1;
                        q.push(y);
                    }
                }
                t[i] = temp;
            }
        }
    }
    
    int main()
    {
    
    
        for(int i=1001; i<10000; i++)
        {
            if(Prime(i))
            {
                check[i] = true;
            }
        }
    
        cin>> n;
        for(int i=0; i<n; i++)
        {
            int a, b;
            cin>> a>> b;
            if(a==b)
            {
                cout<< 0<< endl;
                continue ;
            }
    
            vis[a] = true;
            BFS(a, b);
    
        }
    
        return 0;
    }
    

    大意:給你兩個杯子,容量分別是A,B,問你經過多少次的操作,能夠使其中的一個杯子中的水量是C;一共有3種操作,分別是裝滿,全倒掉,把i杯子中的水倒給j杯子;
    這道題需要依次輸出倒水的方法,也就是需要記錄路徑,採用的是記錄前驅的方法來對可以完成目標的方法進行連接的。最後根據鏈接從後向前將元素入棧,之後再依次出棧。

    #include<iostream>
    #include<queue>
    #include<stack>
    #include<string.h>
    using namespace std;
    
    typedef struct
    {
        int a, b;//狀態
        int step;//步數
        int oper;//操作
        int current;//當前位置
        int parent;//前驅
    }node;
    
    node cond[100000]; int c;//配套下標
    queue<node> q;
    stack<node> S;
    bool vis[105][105];
    
    
    
    int A, B, C;
    int s=1; //步數
    node m; //臨時
    
    void Init(node &n,int a,int b, int s, int o, int c, int p)
    {
        n.a = a;    n.b = b;    n.step = s;     n.oper = o;     n.current = c;  n.parent = p;
    }
    
    bool Operation(node& cur)
    {
        for(int i=0; i<6; i++)
        {
            c++;
            if(i==0)//A滿
            {
                Init(cond[c], A, cur.b, cur.step+1, 0, c, cur.current);
            }
            else if(i==1)//B滿
            {
                Init(cond[c], cur.a, B, cur.step+1, 1, c, cur.current);
            }
            else if(i==2)//A空
            {
                Init(cond[c], 0, cur.b, cur.step+1, 2, c, cur.current);
            }
            else if(i==3)//B空
            {
                Init(cond[c], cur.a, 0, cur.step+1, 3, c, cur.current);
            }
            else if(i==4)//A->B
            {
                if((B-cur.b)>=cur.a)//A全部倒入B
                {
                    Init(cond[c], 0, cur.b+cur.a, cur.step+1, 4, c, cur.current);
                }
                else
                {
                    Init(cond[c], cur.a-(B-cur.b), B, cur.step+1, 4, c, cur.current);
                }
            }
            else /*if(i==5)//B->A*/
            {
                if((A-cur.a)>=cur.b)//B全部倒入A
                {
                    Init(cond[c], cur.b+cur.a, 0, cur.step+1, 5, c, cur.current);
                }
                else
                {
                    Init(cond[c], A, cur.b-(A-cur.a), cur.step+1, 5, c, cur.current);
                }
            }
    
            if(cond[c].a==C || cond[c].b==C)
            {
                m = cond[c];
                return true;
            }
            if(!vis[cond[c].a][cond[c].b])
            {
                vis[cond[c].a][cond[c].b] = true;
                q.push(cond[c]);
            }
    
        }
        return false;
    }
    
    bool BFS(int A, int B, int C)
    {
        c = 0;
        Init(cond[c], 0, 0, 0, 0, 0, 0);
        q.push(cond[c]);
        vis[0][0] = true;
    
        while(!q.empty())
        {
            node cur;
            cur = q.front();    q.pop();
    
            if(Operation(cur)) return true;
    
        }
    
        return false;
    }
    
    void Output()
    {
        cout<< m.step<< endl;
    
        while(m.current>0)
        {
            S.push(m);
            m = cond[m.parent];
        }
    
        while(!S.empty())
        {
            switch((S.top()).oper)
            {
            case 0:
                cout<<"FILL(1)"<<endl;
                break;
            case 1:
                cout<<"FILL(2)"<<endl;
                break;
            case 2:
                cout<<"DROP(1)"<<endl;
                break;
            case 3:
                cout<<"DROP(2)"<<endl;
                break;
            case 4:
                cout<<"POUR(1,2)"<<endl;
                break;
            case 5:
                cout<<"POUR(2,1)"<<endl;
                break;
            }
            S.pop();
        }
    }
    
    int main()
    {
        cin>> A>> B>>C;
    
        memset(cond, 0, sizeof(cond));
        memset(vis, false, sizeof(vis));
    
        if(BFS(A, B, C)) Output();
        else cout<< "impossible";
    
        return 0;
    }
    

    大意:給你一塊地,W表示有水的地方,‘.’表示地是乾的,若一個W與周圍的W緊挨着則他們是一整塊池塘,問你一共有多少塊池塘
    從第一行第一列開始掃,遇到W就進入dfs,向八個方向搜索,可以連接在一起形成池塘的水坑進行標記。

        #include<iostream>
        #include<string.h>
        #include<cstdlib>
        #include<cstdio>
        using namespace std;
        #define MAXN 101
    
        char pool[MAXN][MAXN];
        int N, M;
        int ans;
        int d[8][2]={{0, 1},{0, -1},{-1,0},{1,0},{-1,1},{1,1},{-1,-1},{1,-1}};
    
        void DFS(int x, int y)
        {
            if(pool[x][y] == '.') return ;
    
            pool[x][y] = '.';
            for(int i=0; i<8; i++)
            {
                int nx = x+d[i][0];
                int ny = y+d[i][1];
    
                if(nx>=0 && nx<N && ny>=0 && ny<M)
                {
                    DFS(nx, ny);
                }
            }
        }
    
        int main()
        {
            cin>> N>> M;
    
            for(int i=0; i<N; i++)
            {
                getchar();
                for(int j=0; j<M; j++)
                {
                    scanf("%c", &pool[i][j]);
                }
            }
    
            for(int i=0; i<N; i++)
            {
                for(int j=0; j<M; j++)
                {
    
                    if(pool[i][j] == 'W')
                    {
                        ans++;
                        DFS(i, j);
                    }
                }
            }
    
            cout<< ans;
            return 0;
        }
    
發佈了9 篇原創文章 · 獲贊 1 · 訪問量 1351
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章