宽度优先搜索(广度优先搜索&&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;
        }
    
发布了7 篇原创文章 · 获赞 1 · 访问量 530

猜你喜欢

转载自blog.csdn.net/weixin_44193527/article/details/103376662