【算法学习系列第二章】广度优先遍历思路及其经典例题讲解

本文旨在帮助各位小伙伴理清楚BFS的算法思路,以及它是如何被应用到解题过程中的。


BFS

广度优先遍历(BFS)是一种算法思想,主要利用的数据结构是队列,主要是在搜索的过程中把离当前位置最近的一系列节点先入队,
每次取出队头元素,将扩展出的所有元素放到队尾。广度优先遍历是一层一层的搜。

一、BFS搜索模板思路

1.定义队列;
2.初始状态入队;
3.while(q.size())//队列不空
{
   auto t=q.front();//取出队头
   q.pop();//弹出队头;
  //扩展队头;
  q.push(...);//把符合条件的节点加入队列 
}

二、BFS具体应用

1.BFS求解最小值

马的遍历

在这里插入图片描述

具体思路就是搜索的时候把相应的节点入队,每次取出队头的时候记得要弹出队头,如果在扩展队头的时候发现走到该点了,就返回最小步数。

以下是AC代码

#include<bits/stdc++.h>

using namespace std;
const int N=410;
typedef pair<int,int> PII;
int n,m;
PII start;
int a[N][N];
bool st[N][N];
int dx[8]={-2,-2,-1,-1,2,2,1,1};
int dy[8]={1,-1,2,-2,1,-1,2,-2};
bool check(int x,int y){
    if(x<=0||x>n|y<=0||y>m) return false;
    if(st[x][y]) return false;
    return true;
}
void bfs(){
    queue<PII> q;
    q.push(start);
    st[start.first][start.second]=true;
    while(q.size()){
        auto t=q.front();
        q.pop();
        for(int i=0;i<8;i++){
            int tx=t.first+dx[i],ty=t.second+dy[i];
            if(check(tx,ty)){
                PII temp={tx,ty};
                q.push(temp);
                a[tx][ty]=a[t.first][t.second]+1;
                st[tx][ty]=true;
            }
        }
    }
}
int main(){
    cin>>n>>m>>start.first>>start.second;
    memset(a,-1,sizeof a);
    a[start.first][start.second]=0;
    bfs();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            printf("%-5d",a[i][j]);
        }
        cout<<endl;
    }
    return 0;
}


2.Meteor Shower

在这里插入图片描述
个人觉得本题主要注意的是时间的处理,时间在变化,如何处理时间变化对格子的影响?这里采用的是存储每个格子的最早被烧焦时间的方法。
代码如下:

#include<bits/stdc++.h>

using namespace std;
const int N=1010,INF=0x4f4f4f4f;
int m;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
int Time[N][N];
bool st[N][N];
struct node{
    int x;
    int y;
    int time;
};
int bfs(node start){
    queue<node> q;
    q.push(start);
    st[start.x][start.y]=true;
    while(!q.empty()){
        auto t=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            node n;
            n.x=t.x+dx[i],n.y=t.y+dy[i];
            n.time=t.time+1;

            if(n.x<0||n.y<0) continue;
            if(Time[n.x][n.y]==INF) return n.time;
            if(n.time<Time[n.x][n.y]&&!st[n.x][n.y]){
                q.push(n);
                st[n.x][n.y]=true;
            }
        }

    }
    return -1;
}
int main(){
    //该格子最早会被流星砸到的时间
    cin>>m;
    memset(Time,0x4f,sizeof Time);
    for(int i=0;i<m;i++){
        int x,y,tim;
        cin>>x>>y>>tim;
        Time[x][y]=min(Time[x][y],tim);//更新该位置的最早到达时间
        for(int j=0;j<4;j++){
            int tx=x+dx[j],ty=y+dy[j];
            if(tx<0||ty<0||tx>300||ty>300) continue;
            Time[tx][ty]=min(Time[tx][ty],tim);//更新该位置的周围位置的最早到达时间
        }
    }
    node start;
    start.x=start.y=start.time=0;
     int ans=bfs(start);
     cout<<ans<<endl;
    return 0;
}

3.穿越雷区

请添加图片描述
大家可以看到BFS的有关题目大都是非常有规律的。但是,BFS的代码非常的长,思路比较简单,每一次扩展一层。用一个st数组来表示该点有没有入过队。思路就是利用结构体存储到该点的步数。每一次扩展一层符合条件的节点之前判断一下是否搜到了终点。当然你也可以把判断终点的代码写到for循环里。
BFS在求最短路方面有显著的优点。
代码如下:

#include<bits/stdc++.h>

using namespace std;
typedef pair<int,int> PII;
struct node{
    int x;
    int y;
    int cnt;//从起点走到x,y这个点走了cnt步
};
node start;
PII End;
const int N=110;
char g[N][N];
int n;
int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};
bool st[N][N];
int bfs(node start){
    queue<node> q;
    q.push(start);
    st[start.x][start.y]= true;
    while(q.size()){
        auto t=q.front();
        if(t.x==End.first&&t.y==End.second){
            return t.cnt;
        }
        q.pop();
        for(int i=0;i<4;i++){
            int tx=t.x+dx[i],ty=t.y+dy[i];
            if(tx<1||tx>n||ty<1||ty>n) continue;
            if(st[tx][ty]) continue;
            if(g[tx][ty]!=g[t.x][t.y]){
                node x={tx,ty,t.cnt+1};
                q.push(x);
                st[tx][ty]=true;

            }
        }
    }
    return -1;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>g[i][j];
            if(g[i][j]=='A') start={i,j,0};
            if(g[i][j]=='B') End={i,j};
        }
    }

    cout<<bfs(start)<<endl;


    return 0;
}

4.迷宫(最短路)

在这里插入图片描述

内容与下面的文本相同)
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个字符串,包含四种字母 D、U、L、R,在提交答案时只填写这个字符串,填
写多余的内容将无法得分。

本题思路很简单,唯一的难点在于如何把DULR转换成我们熟悉的偏移量数组,这里采用的是利用结构体存储x,y这个点的坐标,以及从起点走到x,y 的步数,和走到x,y的字符串(每走到一个符合条件的节点,就把该节点所在的方向拼接到原来的字符串当中)需要注意的是注意题中要求输出字典序最小的,所以在定义方向数组dir的时候定义成D,L,R,U,偏移量数组再定义成对应的下,左,右,上就行了。
细心细心再细心,我忘了看字典序呜呜
代码如下:

#include<bits/stdc++.h>

using namespace std;
const int N=60;
char g[N][N];
char dir[4]={'D','L','R','U'};//方向数组
int dx[4]={1,0,0,-1};
int dy[4]={0,-1,1,0};//偏移量数组
int n,m;//n行m列
struct node{
    int x;
    int y;
    int cnt;
    string s;
};
bool st[N][N];
node start={0,0,0,""};
string bfs(node start){
    queue<node> q;
    q.push(start);
    st[start.x][start.y]=true;
    while(q.size()){
        auto t=q.front();
        if(t.x==n-1&&t.y==m-1){
            return t.s;
        }
        q.pop();
        for(int i=0;i<4;i++){
            int tx=t.x+dx[i],ty=t.y+dy[i];
            if(tx<0||tx>=n||ty<0||ty>=m) continue;
            if(st[tx][ty]) continue;
            if(g[tx][ty]=='1') continue;
            node x={tx,ty,t.cnt+1,t.s+dir[i]};
            q.push(x);
            st[tx][ty]=true;
        }
    }
}
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(start)<<endl;

    return 0;
}

总结

关于BFS就介绍到这里,希望对你有所帮助。后续有问题会持续更新。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值