bfs算法总结,通过bfs解决洛谷中部分例题

本文介绍了BFS算法的基本概念,通过迷宫问题和两个编程实例展示了如何在C/C++中实现BFS,包括马的遍历和流星雨安全区域问题。作者强调算法步骤和队列在BFS中的关键作用。
摘要由CSDN通过智能技术生成

 (本篇博客中的代码均是c语言代码,c++正在学......)

一.bfs算法的介绍。

资料中是这样解释bfs算法的。

宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

(简单来说,bfs是一种通过盲目搜索的方法,搜索图中的所有节点,找到最终答案。而不考虑结果的位置,完整的搜索整张图,直到找到结果为止。)

还有很多不同的理解如:

宽度优先搜索是一种对图进行搜索的算法。假设我们一开始位于某个顶点(即起点),此时并不知道图的整体结构,而我们的目的是从起点开始顺着边搜索,直到到达指定顶点(即终点)。在此过程中每走到一个顶点,就会判断一次它是否为终点。广度优先搜索会优先从离起点 近的顶点开始搜索。

https://blog.csdn.net/aliyonghang/article/details/128724989(原文摘自)

 

 我们可以将bfs和dfs的过程图对比一下,可以比较两者的差别,也可以帮助我们理解bfs的搜索过程。(左边是dfs,右边的bfs)

 我们难看出,bfs的搜索过程和队列具有相似性,因而我们常常通过队列的形式模拟bfs的搜索过程。

最后如果想要详细的学习bfs的搜索过程,以及掌握其使用方法可以看这篇博客

https://blog.csdn.net/aliyonghang/article/details/128724989

二.通过迷宫问题理解bfs的搜索过程。

之前我分析过dfs求解迷宫问题,但在这里,我就省个事,推送一个非常详细的视频,肯定比我讲的的要透彻很多(非常容易理解)。

bfs迷宫问题详细讲解

三.例题分析。

一.马的遍历。

https://www.luogu.com.cn/problem/P1443

题目描述

有一个 n×m 的棋盘,在某个点 (x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。

输入格式

输入只有一行四个整数,分别为 n,m,x,y。

输出格式

一个 n×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 −1−1)。

输入输出样例

输入 #1复制

3 3 1 1

输出 #1复制

0    3    2    
3    -1   1    
2    1    4    

说明/提示

对于全部的测试点,保证 1≤x≤n≤400,1≤y≤m≤400。

对于本题,其实没有什么难点,其实就是把棋盘上的每一个点按照规则入队,第一次到达该点时的步数一定是最优步数,唯一需要注意的点就是将马能到达的点用数组表示清楚就可以了。其他地方就和正常广搜没有差不多。

直接上代码吧(话都在代码里)

#include<stdio.h>
#include<string.h>
struct queue{               //通过结构体建立队列
    int x;
    int y;
};
int main()
{
    int n,m,startx,starty;
    scanf("%d %d %d %d",&n,&m,&startx,&starty);
    int a[401][401];             //录入棋盘
    int vis[401][401]={0};       //标记数组
    memset(a,-1,sizeof(a));         //将所有点都设为-1,最后马不能到达的点就是-1的点
    struct queue r[96000];
    int head=1,tail=1;
    r[tail].x=startx;            //将起点入队
    r[tail].y=starty;
    tail++;
    a[startx][starty]=0;         
    vis[startx][starty]=1;       //将起点标记
    int next1[8]={-2,-2,2,2,1,-1,1,-1};        //马能行动的位置,类似与方向数组
    int next2[8]={-1,1,-1,1,2,-2,-2,2};
    while(head<tail)             //不为空队
    {
        for(int i=0;i<8;i++)
        {
            int dx=r[head].x+next1[i];
            int dy=r[head].y+next2[i];
            if(dx<1||dx>n||dy<1||dy>m)        //边界条件
                continue;
            if(vis[dx][dy]==0)
            {
                r[tail].x=dx;                 //入队操作
                r[tail].y=dy;
                a[dx][dy]=a[r[head].x][r[head].y]+1;
                vis[dx][dy]=1;
                tail++;
            }
        }
        head++;                             //不要忘记,搜索完后要出队
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            printf("%d ",a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

二.Meteor Shower S。

https://www.luogu.com.cn/problem/P2895

题目描述

贝茜听说一场特别的流星雨即将到来:这些流星会撞向地球,并摧毁它们所撞击的任何东西。她为自己的安全感到焦虑,发誓要找到一个安全的地方(一个永远不会被流星摧毁的地方)。

如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。

根据预报,一共有 M 颗流星 (1≤M≤50,000) 会坠落在农场上,其中第 i 颗流星会在时刻 Ti​(0≤Ti​≤1000)砸在坐标为(Xi​,Yi​)(0≤Xi​≤300,0≤Yi​≤300) 的格子里。流星的力量会将它所在的格子,以及周围 44个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。

贝茜在时刻 0 开始行动,她只能在第一象限中,平行于坐标轴行动,每 1 个时刻中,她能移动到相邻的(一般是 4 个)格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻 t 被流星撞击或烧焦,那么贝茜只能在 t 之前的时刻在这个格子里出现。 贝茜一开始在 (0,0)。

请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 −1。

输入格式

共 M+1 行,第 1 行输入一个整数 M,接下来的 M 行每行输入三个整数分别为 Xi​,Yi​,Ti​。

输出格式

贝茜到达安全地点所需的最短时间,如果不可能,则为 −1。

输入输出样例

输入 #1复制

4
0 0 2
2 1 2
1 1 2
0 3 5

输出 #1复制

5

对于这题难道明显就上升了许多,(主要是坑点太多),如果单纯从解题思路来看,这题与迷宫问题差不多,但是本题加入了时间的判断,让我一开始拿到这题无从下手。其思路为:陨石地图用一个二维数组记录,内容为砸下时间,以时间早为标准;再用一个二维数组记录每个点最短时间;终止条件:如果搜到一个点永远不会被陨石砸到,输出该点时间,或者直到搜索结束也没有出去,输出-1。

注意点:

1.坐标不能低于0,但可以超300!2.流星定时砸下;3.流星砸下时间已最早的那个为准!4.如果出不去还要输出-1!

#include<stdio.h>
#include<string.h>
struct queue{                  //常规建立队列
    int x;
    int y;
    int time;                  //记录时间
};
int main()
{
    int a[305][305],vis[305][305]={0};     //a数组存放地图,内容为陨石落下的时间
    int n,x1,y1,t;
    scanf("%d",&n);
    int next1[5]={0,0,1,0,-1};             //方向数组
    int next2[5]={0,1,0,-1,0};
    memset(a,-1,sizeof(a));                //将数组中值全部赋值为-1,方便最后输出
    for(int i=0;i<n;i++)                   
    {
        scanf("%d%d%d",&x1,&y1,&t);
        for(int j=0;j<5;j++)
        {
            if(x1+next1[j]>=0&&y1+next2[j]>=0&&(a[x1+next1[j]][y1+next2[j]]==-1||a[x1+next1[j]][y1+next2[j]]>t))   //如果该标记x,y坐标大于-1且该点没有被陨 
               a[x1+next1[j]][y1+next2[j]]=t;        //石砸落或已标记时间没有该时间早
        }                                            //标记陨石
    }
    struct queue r[90005];
    int head=1,tail=1;
    r[tail].x=0;                          //将起点入队
    r[tail].y=0;
    r[tail].time=0;
    tail++;                               
    vis[0][0]=1;
    while(head<tail)                      //下面的搜索代码都大差不差
    {
        for(int i=1;i<=4;i++)
        {
            int dx=r[head].x+next1[i];
            int dy=r[head].y+next2[i];
            if(dx<0||dy<0)                //边界条件,能走到地图外
                continue;
            if(vis[dx][dy]==0&&(a[dx][dy]>r[head].time+1||a[dx][dy]==-1))
            {
                r[tail].x=dx;
                r[tail].y=dy;
                r[tail].time=r[head].time+1;
                vis[dx][dy]=1;
                if(a[dx][dy]==-1)             //终止条件,找到安全地带
                {
                    printf("%d\n",r[tail].time);
                    return 0;
                }
                tail++;
            }
        }
        head++;
    }
    printf("-1\n");                      //没有找到安全地带
    return 0;
}

最后因为时间问题,例题分析就先到这里了 ,以后有机会再给大家分享。

四.bfs算法小结。

算法步骤
(1)把起始节点S线放到queue 表中克祥
  (2)如果queue是空表,则失败退出,否则继续。
  (3)在queue表中取最前面的节点node 移到 CL OSED 表中。(出队)
  (4)扩展node节点。若没有后继即叶节点),则转向(2)循环。
  (5)把node的所有后继节点放在queue表的末端。各后继结点指针指向node节点。(入队)
  (6)若后继节点中某一个是目标节点,则找到一个解,成功退出。否则转向(2)循环                
原文链接:https://blog.csdn.net/zgdlxs/article/details/122747226

最后,又到了我的宇宙免责声明 环节了:关于本篇博客是我(新手小白)对于bfs的算法的一些粗陋的认识,无论是认识还是代码中,肯定会有一些错误,欢迎大家指正,给予意见。如果本篇小结不符合您的认知,亦或者对本篇博客不满意,可以将其当做笑话。最后,希望和大家一起进步,一同努力。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值