(c语言实现)算法笔记之bfs及pta习题

2023-10-26

目录

一,bfs(广度优先搜索)的定义

二,bfs(广度优先搜索)的应用

三,题型训练

1,奇怪的电梯

2,寻宝 

3,龙舌兰酒吧 

四,总结 



       

一,bfs(广度优先搜索)的定义

BFS 全称是 Breadth First Search,中文名是宽度优先搜索,也叫广度优先搜索。

是图上最基础、最重要的搜索算法之一。

所谓宽度优先。就是每次都尝试访问同一层的节点。 如果同一层都访问完了,再访问下一层。

这样做的结果是,BFS 算法找到的路径是从起点开始的 最短 合法路径。换言之,这条路径所包含的边数最小。

在 BFS 结束时,每个节点都是通过从起点到该点的最短路径访问的。

算法过程可以看做是图上火苗传播的过程:最开始只有起点着火了,在每一时刻,有火的节点都向它相邻的所有节点传播火苗。


以上资料来自于BFS(图论) - OI Wiki (oi-wiki.org)

 说点人话环节(doge):比如走迷宫,我们一开始肯定会有一个起始位置,而之后改如何走是不太确定的,因为有四个方向可以走,广搜就是,一次搜索就走这四个方向,搜过的点就标记一下,下次不要再回去找(所以这里会标记起始的点,而由起始点走出的四个点还没有搜索,所以不必标记)。之后再从走出的四个点分别进行搜索接下去的四个方向的点。由一次搜索走出来的所有点就是一层。在每次走完一层之后,就先判定一下这一层的点有没有所需要的答案,如果有,那么就不需要再找了。

在图上就是,以V1为起点,第一次搜索就搜V2,V3,并将V1标记。此为一层,这时,应该先判断一下V2,V3是不是我们所需要的点,如果不是,那么就继续,往下搜索V4,V5,V6,V7. 直到找到目标。

那这时候就有人要问了:那找不到怎么办?也有走不通的迷宫啊!

回答这个问题之前,我们要先想想,bfs搜索的时候,数据要放到哪里?我们想想,每一次搜索的时候是不是就是先将要搜索的点放入储存的结构中,然后搜索完标记并退出。这符合“先进先出”的特点,也就是队列的特点。由于只会c语言我们只能去模拟队列的实现。如何模拟呢?用一个数组和两个指针变量就可以了。

    struct node queque[10000];
    memset(queque,0,sizeof(queque));
    int head=0,end=1;

就可以模拟队列的实现,当然,如果要使用模拟循环队列也是可以的。(记得加个判断,end>数组长度时,重置即可)但是要主要开好足够的空间 ,不然可能还没搜索,那一层的部分数据就会被覆盖掉。

因为一层一层搜索,所以如果还能走,必然有数据入队,自然就不会出现head>=end的情况(如果是循环队列,那么判断条件应该是head!=end,即head不会与end相等)。一旦出现上述情况,那么就说明没得搜了,已经都走遍了,如果此时还没找到答案,那么就是没有答案了。

二,bfs(广度优先搜索)的应用

目前我所遇到的基本上bfs的应用在于寻找最短路径类,之后如果有遇到其他的应用,会再写一篇文章来描述新的应用。

那么这里就以走迷宫为例,看看如何实现吧。

首先肯定要创建好结构体记录每次走的点,并创建好队列

struct node
{
    int num;
    int i;
    int j;
};

    struct node queque[10000];
    memset(queque,0,sizeof(queque));
    int head=0,end=1;

 然后创建走的四个方向

int local[][2]=
{
    {0,1},
    {0,-1},
    {1,0},
    {-1,0}
};

之后就是主体部分的实现了

int tmp=end;
        while(head<tmp)//一层搜索
        {
           for(int i=0;i<4;i++)//每个点搜索四个方位
           {
               int tmpi=queque[head].i+local[i][0];
               int tmpj=queque[head].j+local[i][1];
               if(tmpi<0||tmpi>=n||tmpj<0||tmpj>=m)//判断边界条件
               {
                   continue;
               }
               else if(arr[tmpi][tmpj]==-1)//搜索过的或者不可走点
               {
                   continue;
               }
               else
               {
                   //存下数值和地址,并将其入队
                   queque[end].num=arr[tmpi][tmpj];
                   queque[end].i=tmpi;
                   queque[end].j=tmpj;
                   end++;
                   arr[tmpi][tmpj]=-1;
               }
           }
            head++;
        }

三,题型训练

1,奇怪的电梯

 c大楼有一个一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼 (1≤i≤N) 上有一个数字Ki(0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3,3,1,2,5 代表了Ki (K1=3,K2=3,…),在1楼,按“上”可以到4楼,按“下”是不起作用的,因为没有−2楼。那么,从A楼到B楼至少要按几次按钮呢?

输入格式:

第一行包含3个用空格隔开的正整数,分别表示N,A,B (1≤N≤200,1≤A,B≤N) 。 第二行包含N 个用空格隔开的非负整数,表示Ki 。

输出格式:

输出共一行,即最少按键次数,若无法到达,则输出 −1 。

输入样例:

5 1 5
3 3 1 2 5

输出样例:

3

这道题也是典型bfs的题目,具体的实现即解释也在代码中

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int judge(int arr[])
{
    int i=0;
    for(i=0;i<2000;i++)
    {
        if(arr[i]!=0)
            return 1;
    }
    return 0;
}
int main()
{
    int n;
    scanf("%d",&n);
    int arr[n+1];
    int i=0;
    int sta,end;
    scanf("%d%d",&sta,&end);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&arr[i]);
    }
    arr[0]=0;
    int queque[2000];//模拟队列的实现
    memset(queque,0,sizeof(queque));
    int top=0,bottom=1;
    queque[top]=sta;
    int count=0;
    int flag=0;
    while(judge(queque))
    {
        //广搜对一层的搜索
        int tmp=bottom;
        while(top<tmp)
        {
           //此处节点标记采用arr[i]=0方式
            if(arr[queque[top]]!=0)
            {
               if(queque[top]+arr[queque[top]]<=n)
               {
                //对上楼层节点进行访问,将可访问节点放入队列并标记该节点
                queque[bottom++]=queque[top]+arr[queque[top]];
                //arr[queque[top]+arr[queque[top]]]=0;
               }
               if(queque[top]-arr[queque[top]]>0)
               {
                //对下楼层进行访问,将可访问节点放入队列并标记该节点
                queque[bottom++]=queque[top]-arr[queque[top]];
               }
                arr[queque[top]]=0;
            }
            top++;
        }
        //搜索一次就需要按一次电梯
        count++;
        //判断目前的层数中有无目标数
        int i=0;
        for(;i<top;i++)
        {
            if(queque[i]==end)
            {
                flag=1;
                 goto ending;
            }
            queque[i]=0;
        }
    }
    ending:
    if(flag)
    printf("%d",count-1);
    else
    printf("-1");
    return 0;
}

2,寻宝 

奕哥今天玩到一款寻宝游戏,地图是一个n*m的矩阵,其中分布着一些宝藏,每个宝藏具有一定的价值,奕哥只能拿走其中一个宝藏。奕哥起始在a行b列。奕哥可以向相邻的一格移动,但不能走出地图外。奕哥初始体力值X,移动一格需要消耗体力值为1。体力耗尽后奕哥无法继续移动。地图中有一些障碍物,奕哥无法移动到障碍物上。奕哥想知道他能拿到的最具价值的宝藏是哪一个。

输入格式:

第一行包含5个整数n,m,a,b,X。n,m分别表示地图行数,列数;a,b表示奕哥起始位置在a行b列;X表示奕哥的体力。

( 1<=n,m<=1000, 1<=a<=n, 1<=b<=m, 0<=X<=1e10)

接下来n行,每行m个整数。第i行第j个整数为Aij (-1<=Aij<=1e9)。若Aij=-1,则表示第i行第j列有障碍物;否则表示该位置有一个价值为Aij的宝物。保证起始位置没有障碍物。

输出格式:

奕哥能拿到价值最高的宝藏的价值。

输入样例:

3 3 1 1 3
0 -1 10
3 5 7
-1 8 15

输出样例:

8

这就是迷宫题了,上面的点已经说得清楚了,不懂的可以再上去看看

#include<stdio.h>
#include<string.h>
int local[][2]=
{
    {0,1},
    {0,-1},
    {1,0},
    {-1,0}
};
struct node
{
    int num;
    int i;
    int j;
};
int main()
{
	int n, m, a, b, x;
	scanf("%d%d%d%d%d", &n, &m, &a, &b, &x);
	a--;//数组中初始位置要减一
	b--;
	int arr[n][m];//读入地图
    int i=0;
	for (i = 0; i<n; i++)
	{
		int j = 0;
		for (j = 0; j<m; j++)
		{
			scanf("%d", &arr[i][j]);
		}
	}
    struct node queque[10000];
    memset(queque,0,sizeof(queque));
    int head=0,end=1;
    queque[head].num=arr[a][b];
    queque[head].i=a;//记录对应数值的坐标
    queque[head].j=b;
    arr[a][b]=-1;//搜索过的就直接标记
    while(x--)
    {
        int tmp=end;
        while(head<tmp)//一层搜索
        {
           for(int i=0;i<4;i++)//每个点搜索四个方位
           {
               int tmpi=queque[head].i+local[i][0];
               int tmpj=queque[head].j+local[i][1];
               if(tmpi<0||tmpi>=n||tmpj<0||tmpj>=m)//判断边界条件
               {
                   continue;
               }
               else if(arr[tmpi][tmpj]==-1)//搜索过的或者不可走点
               {
                   continue;
               }
               else
               {
                   //存下数值和地址,并将其入队
                   queque[end].num=arr[tmpi][tmpj];
                   queque[end].i=tmpi;
                   queque[end].j=tmpj;
                   end++;
                   arr[tmpi][tmpj]=-1;
               }
           }
            head++;
        }
    }
    int max=0;
    for(int i=0;i<end;i++)
    {
        if(queque[i].num>max)
            max=queque[i].num;
    }
    printf("%d",max);
	return 0;
}

3,龙舌兰酒吧 

有一个大小为n*m的矩形小镇,城镇上有房屋(“#”表示无法通过),有空地(“.”表示可通行),每次移动只能朝上下左右四个方向,且需花费1单位时间。

一天,二乔和承太郎约定在龙舌兰酒吧见面,两人同时从各自所在位置向酒吧出发。请问最少需要过多少时间他们才能在酒吧碰面。

地图上P表示二乔的位置,W表示承太郎的位置,B表示酒吧的位置。

输入格式:

第一行包含两个整数n,m,表示城镇的大小。(1<=m,n<=1000)。

接下来n行,每行m个字符,表示小镇的情况。

输出格式:

输出两人在酒吧碰面的最少花费时间,如果无法在酒吧碰面则输出-1。

输入样例:

5 4
.W.#
P#..
....
B..#
#...

输出样例:

4

具体代码实现如下:

#include<stdio.h>
#include<string.h>
//四个方位
int local[][2]={
    {0,1},
    {0,-1},
    {1,0},
    {-1,0}
};
struct node
{
    char ch;
    int i;
    int j;
};
int judge(struct node *queque,int n)
{
    int i=0;
    for(;i<n;i++)
    {
        if(queque[i].ch!=0)
            return 1;
    }
    return 0;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    char arrp[n][m];//供p寻找
    char arrw[n][m];//供w寻找
    struct node queque[600000];//模拟队列实现
    memset(queque,0,sizeof(queque));
    for(int i=0;i<n;i++)
    {
        getchar();
        for(int j=0;j<m;j++)
        {
            scanf("%c",&arrp[i][j]);
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
            arrw[i][j]=arrp[i][j];
    }
    int pi,pj,wi,wj,tari,tarj;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(arrp[i][j]=='P')//找到P坐标
            {
                pi=i;
                pj=j;
            }
            if(arrp[i][j]=='W')//找到W坐标
            {
                wi=i;
                wj=j;
            }
        }
    }
    
  /*
  此处为测试arrp和arrw是否正常 
         for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            printf("%c",arrw[i][j]);
        }
        printf("\n");
    }
   */
    int countp=0;
    int countw=0;
    int flag1=0;
    //此处开始为两次bfs的实现
    //1.arrp的路径寻找
    int head=0;
    int end=1;
    queque[head].ch=arrp[pi][pj];
    queque[head].i=pi;
    queque[head].j=pj;
    while(judge(queque,end))//判断队列是否为空
    {
        int tmp=end;
        while(head<tmp)
        {
          for(int i=0;i<4;i++)
          {
              int tmpi=queque[head].i+local[i][0];
              int tmpj=queque[head].j+local[i][1];
              if(tmpi<0||tmpi>=n||tmpj<0||tmpj>=m)
                  continue;
              else if(arrp[tmpi][tmpj]=='#')
                  continue;
              else
              {
                  queque[end].ch=arrp[tmpi][tmpj];
                  queque[end].i=tmpi;
                  queque[end].j=tmpj;
                  arrp[tmpi][tmpj]='#';
                  end++;
              }
          }
            queque[head].ch=0;//搜索完出队列
            head++;
        }
        countp++;//搜索完一次走一步
        for(int i=head;i<end;i++)
        {
            if(queque[i].ch=='B')
            {
                flag1=1;
                break;
            }
        }
        if(flag1)
            break;
    }
    memset(queque,0,sizeof(queque));
    head=0;
    end=1;
    queque[head].ch=arrw[wi][wj];
    queque[head].i=wi;
    queque[head].j=wj;
    //2.arrw路径寻找
    int flag2=0;
    while(judge(queque,end))//判断队列是否为空
    {
        int tmp=end;
        while(head<tmp)
        {
          for(int i=0;i<4;i++)
          {
              int tmpi=queque[head].i+local[i][0];
              int tmpj=queque[head].j+local[i][1];
              if(tmpi<0||tmpi>=n||tmpj<0||tmpj>=m)
                  continue;
              else if(arrw[tmpi][tmpj]=='#')
                  continue;
              else
              {
                  queque[end].ch=arrw[tmpi][tmpj];
                  queque[end].i=tmpi;
                  queque[end].j=tmpj;
                  arrw[tmpi][tmpj]='#';
                  end++;
              }
          }
            queque[head].ch=0;
            head++;
        }
        countw++;
        for(int i=head;i<end;i++)
        {
            if(queque[i].ch=='B')
            {
                flag2=1;
                break;
            }
        }
        if(flag2)
            break;
    }
    if(flag1==0||flag2==0)
        printf("-1");
    else
    {
        printf("%d",countp>countw?countp:countw);
    }
    return 0;
}

四,总结 

总体来说,bfs理解了就会很难(doge),bfs也是图论的一个基础算法,希望这篇文章能帮助到你掌握bfs,看到这里不妨给我点个赞吧。之后大概就是一周一篇了。要多输入,才能有输出嘛,多数时间还是要拿来学习的。(还是太菜)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(c语言实现)算法笔记之bfs及pta习题 的相关文章

随机推荐

  • 【解决】已root, 提示‘adbd cannot run as root in production builds‘,adb push /adb pull 到系统目录 解决方案

    手机已root 执行adb root提示 adbd cannot run as root in production builds 替代解决方案如下 脚本我已经写好了adbpush sh adbpull sh adbpull bat adb
  • openwrt编译问题:pip._vendor.urllib3.exceptions.ReadTimeoutError:

    在openwrt 编译的时候报 pip vendor urllib3 exceptions ReadTimeoutError HTTPSConnectionPool host files pythonhosted org port 443
  • HTTPS科普扫盲帖

    为什么需要https HTTP是明文传输的 也就意味着 介于发送端 接收端中间的任意节点都可以知道你们传输的内容是什么 这些节点可能是路由器 代理等 举个最常见的例子 用户登陆 用户输入账号 密码 采用HTTP的话 只要在代理服务器上做点手
  • Oracle创建用户、角色、授权、建表

    https www cnblogs com roger112 p 7685307 html oracle数据库的权限系统分为系统权限与对象权限 系统权限 database system privilege 可以让用户执行特定的命令集 例如
  • 学习自动化测试该怎么学?6个步骤轻松拿捏

    自动化测试作为脱离手工测试的基本核心内容 其重要性不言而喻了 而且我们来看近期大厂的一些招聘信息显示 基本上自动化测试是必备前提 没有这个基本就不用谈后面的问题了 下面我们通过联想集团的一个软件测试工程师的要求来聊一下具体要怎么学才能掌握这
  • 在stm32上对于火焰模块的应用分析

    在stm32上对于火焰模块的应用分析 一 火焰模块 接线说明 供电 3 3v G 接地 GND AO模拟输入 DO数字输出 以stm32f407举例说明 AO接入 PF7 DO接入 PA4 实际上也可以不接 因为本实例没用到 二 火焰模块的
  • 【matlab】norm的用方法

    matlab norm的用方法 从上面可以得到 对一个向量P 5 0 1 norm 就等于各项的平方和再开根号
  • JAVA和C++的几个主要不同点

    1 指针 JAVA语言让编程者无法找到指针来直接访问内存无指针 并且增添了自动的内存治理功能 从而有效地防止了c c 语言中指针操作失误 如野指针所造成的系统崩溃 但也不是说JAVA没有指针 虚拟机内部还是使用了指针 只是外人不得使用而已
  • 100天精通Python(数据分析篇)——第69天:Pandas常用数据筛选方法(between、isin、loc、iloc)

    文章目录 一 布尔索引 二 between 三 isin 1 单列筛选 2 多列筛选 3 通过字典的形式传递多个条件 4 删除异常值所在行 5 isnotin实现 四 loc iloc 重要 0 创建DataFrame 1 提取行数据 2
  • Jmeter(八) - 从入门到精通 - JMeter配置元件(详解教程)

    1 简介 JMeter配置元件可以用来初始化默认值和变量 读取文件数据 设置公共请求参数 赋予变量值等 以便后续采样器使用 将在其作用域的初始化阶段处理 配置元件 Config Element 提供对静态数据配置的支持 可以为取样器设置默认
  • Flutter之基本路由,命名路由跳转,返回上一页,替换路由和返回根路由——Flutter基础系列

    需求 今天为大家介绍一下Flutter是如何进行页面跳转 路由管理的 一 基本路由 1 基本路由使用 假设我们需要从A页面跳转到basic页面 则我们需要在A页面引入 import basic dart 然后在A页面通过以下方法跳转 Rai
  • C/C++指向二维数组的指针

    1 二维数组 设有整型二维数组a 3 4 如下 0 1 2 3 4 5 6 7 8 9 10 11 它的定义为 int a 3 4 0 1 2 3 4 5 6 7 8 9 10 11 设数组a的首地址为1000 各下标变量的首地址及其值如图
  • Spring 全家通之 SpringMVC 如何传递参数以及返回值的类型

    大家好 我是你们的老朋友 Java 学术趴 最近小编又在整了 Spring 全家桶笔记 笔记会每天定时的进行发放 喜欢的大佬们欢迎收藏点赞关注呦 小编会每天分享的呦 今天给大家带来新的框架技术 SpringMVC Spring MVC 属于
  • 带你全面了解自动化测试框架—从理论到工具

    软件行业正迈向自主 快速 高效的未来 为了跟上这个高速前进的生态系统的步伐 必须加快应用程序的交付时间 但不能以牺牲质量为代价 快速实现质量是必要的 因此质量保证得到了很多关注 为了满足卓越的质量和更快的上市时间的需求 自动化测试将被优先考
  • 这张磁盘有写保护_win10 移动硬盘或U盘清除“被写保护”

    Win10系统取消移动硬盘写保护的方法 呃 这是别人写得不错的文章 我转载一下 发布时间 2016 12 20 发布者 win7之家 慧歌 浏览数 1089 移动硬盘是我们经常会用到的一个存储设备 在使用过程中难免会碰到一些情况 就有用户升
  • 【置顶】Flutter系列、Python系列目录

    Flutter系列 Flutter 1 1 8个Flutter的优势以及为什么要在下一个项目中尝试Flutter Flutter安装与运行 Flutter1 2 在 Windows 10下配置Flutter开发环境 Flutter1 3 在
  • 读书笔记 摘自:《分享经济的爆发》

    读书笔记 摘自 分享经济的爆发 作者 印 阿鲁 萨丹拉彻 赞 誉 创新的实验性与监管的连续性本身存在矛盾 监管者通常需要通过更新现有法律体系使其与创新性服务相适应 否则就会阻碍创新 将分享经济看作市场经济和礼物经济的 过渡态 资本主义和社会
  • 20171010离线赛总结

    题解 第一题 字符连通块 这道题还是比较好想的 首先把每个连通块标记出来 并用第一次扫到的点标记为这个连通块的父节点 接下来要做的就是把一个 周围的连通块连通起来 不过要注意一点 在连通标记的时候不要用memset memset的复杂度是m
  • Windows端CUDA11.3+CUDNN+pytorch环境搭建

    1 显卡驱动的安装 最近 在学习pytorch深度学习 遇到很多的坑 环境配置也出现过问题 忍不住和大家进行分享 现在把环境搭建过程分享给大家 1 1 查看自己的显卡 具体操作 我的电脑 属性 设备管理器 显示适配器 1 2 驱动的下载 安
  • (c语言实现)算法笔记之bfs及pta习题

    目录 一 bfs 广度优先搜索 的定义 二 bfs 广度优先搜索 的应用 三 题型训练 1 奇怪的电梯 2 寻宝 3 龙舌兰酒吧 四 总结 一 bfs 广度优先搜索 的定义 BFS 全称是 Breadth First Search 中文名是