对此题有疑问的小伙伴们,相信你们之前肯定对BFS广度优先搜索算法有所了解,也做过一些较为基础的例题,最经典的问题之一应该是迷宫问题从起点S出发,求到达终点E的最短路径,迷宫问题的解决思路是利用BFS从起点S出发,一层一层往外进行地毯式的搜索,直至到达终点E,即可求出最短路径。可以看出,在迷宫问题中,我们只需要应用一次BFS算法,即可求出最短路径。因为起点S和终点E的坐标都只有一个,即有唯一的起点坐标和终点坐标。

解题思路:

  首先来分析一下问题, 这道题最关键的一个地方就是钥匙被放置在多处,蒜头君有多种选择,那怎么知道哪一条路径(起点->钥匙位置->家)是最短的呢?

        在这里,我就以一个简单地图的例子来说明,为大家讲清楚这个问题                                                如下图,是一个4行6列的地图,S为起点,T为终点,P为钥匙,#为障碍物,不可通行

 分析:从以上地图分析出,要从起点S开始走,然后拿到钥匙P,最后到达终点T,有两条最短路径可以走,这两条路径分别是:

路径一:总共需要9步

路径二:总共需要7步

算出以上两条路径之后,我们还需要取其中最短的一条路径,即5+2=7。所以蒜头君拿钥匙  回家的最短路径是7。

再次分析:以上地图中有两把钥匙,所以我们需要分别求出经过这两把钥匙再回到家的最短路径即求出每把钥匙到家到起点的这两个值,然后将其相加,求出某条最短路径然后将n条回家的路径进行比较,算出蒜头君回家的最短路径。也就是说当算一把钥匙到家和到起点的最短距离时,我们需要应用两次BFS,如果说地图中有非常多把钥匙,那么要进行的BFS次数就非常多了,此时程序会超时。那有没有别的方法可以解决程序超时的问题呢?或者说这道题要怎么优化?

优化的思路:我们可以在BFS的时候将标记数组多开一维度,表示是否已经取得了钥匙的状态。如果到达终点并且取得钥匙的状态被标记,bfs结束,即找到了最短路径。可能你还是无法理解,我们不妨用刚刚那个例子来进行一个讲解,首先,我们有一个4行6列的地图,S为起点,T为终点,.为道路(钥匙和家所在的地方也可以视为道路),#为障碍物,不可通行,如下图所示:

我们来模拟一下BFS的执行过程,大家都知道BFS的执行需要队列和标记数组。为了表示每一个坐标的状态(走到当前坐标时手上是否有钥匙、当前坐标的位置、走到当前坐标时距离起点S经过了多少步),我们需要构建一个结构体struct,结构体的成员包括如下几个:

注意:此处为也可以不为结构体添加构造函数,添加构造函数的目的时方便我们构造结构体变量,不要也行,只不过,构造结构体变量时,就显得复杂一点而已。

模拟步骤:

        第一步:起点S在(4,2)处,将其入队,此时队列里有一个元素,并且设置为起点访问过,此时蒜头君身上没有钥匙,此坐标点所表示的队列元素的值为:

 第二步:将S点周围的点全部入队,我们不妨约定,按照右 下 左 上的方向进行BFS,要注意看图示内容的文字说明,后面的步骤大同小异

        第三步:S点扩展完毕,出队,请注意看图示文字说明 

        第四步:扩展(4,3)点,如下图所示:

        第五步:(4,3)出队,此时队首元素是(3,2),从(3,2)开始扩展

以上第9行第3列写错了,2代表的含义应该是距离S共2步

        第六步:扩展(3,2)点,如下图所示:

        第七步:(3,2)扩展完毕,出队,如下图所示:

        第八步:扩展(3,3)点,如下图所示:

        第九步:(3,3)扩展完毕,出队,如下图所示:

        第十步:(3,1)是队首元素,开始扩展,如下图所示:

        第11步:(3,1)出队,如下图所示

        第12步:(2,2)是队首元素,开始扩展,如下图所示:

        第13步:(2,2)出队,如下图所示

        第14步:(3,4)是队首元素,开始扩展,如下图所示:

        第15步:(3,4)出队,如下图所示

        第16步:(2,1)是队首元素,开始扩展,如下图所示:

        第17步:(2,1)出队,如下图所示

 第18步:(1,2)是队首元素,开始扩展,如下图所示:

        第19步:(1,2)出队,如下图所示

        第20步:(3,5)是队首元素,开始扩展,如下图所示:

        第21步:(3,5)出队,如下图所示

        第21步:(2,4)为队首元素,开始扩展,发现它不能扩展任何点,出队

        第22步:(1,1)为队首元素,开始扩展,注意:从(1,1)这个点开始,蒜头君已经获得了钥匙,所以此时v[x][y][1]这个元素派上用场了,观察图示,你会发现之前用的都是v[x][y][0]

      第23步:(1,1)出队,如下图所示

      第23步:(1,3)为队首元素,开始扩展,没有点可扩展,(1,3)出队,如下图所示

      第24步:(3,6)为队首元素,开始扩展

      第25步:(3,6)出队,如下图所示

      第26步:(2,5)为队首元素,开始扩展,没有元素可扩展,(2,5)出队,如下图所示

      第27步:(1,2)为队首元素,开始扩展

         第28步:(1,2)出队,如图所示:

      第29步:(2,1)为队首元素,开始扩展

    第30步:(2,1)出队,如图所示:

    第31步:(3,5)为队首元素,开始扩展,发现,当扩展到该点左边的点的时候,到达终点,可以直接得出最短路径为7

分析总结:我们发现,整个过程只用了1次BFS,便求出了蒜头君拿到钥匙然后回家的最短路径,因为我们引入了一个关键的数据结构—为标记数组多开了一个维度,v[x][y][0]代表没有钥匙时是否访问了v[x][y][0]=0代表(x,y)点在没有钥匙时,也没有被访问,v[x][y][0]=1代表在没有钥匙时,该点被访问,v[x][y][1]=0代表(x,y)点在有钥匙时,没有被访问,v[x][y][1]=1,代表有钥匙,该点被访问。这一段话要仔细去斟酌。

代码实现1:

#include<bits/stdc++.h>
using namespace std;
/*
算出拿钥匙的路+回家的路的最短距离
思路1:求出钥匙到家到起点的两个值,将二者相加即可,求出最小的即为 最短距离 思路2:较难理解,可以利用excel表格画图
提示:同学们要注意,老师上课讲得一些题目都非常经典,那么都是取自于历年的一些真题,上课以题目为主
通过题目的方式去讲解相关的知识,所以同学们一定要学会相关的一些写法以及相关的一些思路 样例1:
8 10
P.####.#P#
..#..#...#
..#T##.#.#
..........
..##.#####
..........
#####...##
###....S##样例1答案:
21*/
const int N = 2005;
int n,m; //地图大小
int sx,sy,ex,ey; //起点和终点
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; //右 下 左 上
char a[N][N]; //地图是一个二维数组,行列不超过2000
bool v[N][N][2]; //一般搜索都会涉及到一个访问数组,不管是DFS还是BFS,都需要将访问过的点设置为已经访问
// 要特别注意v这个数组的含义,第三维用来标记蒜头君走过某个点的时候是否已经带了钥匙
struct node
{int x,y; //坐标int step;bool flag; //钥匙的状态,1代表有钥匙,0代表没有钥匙  //为了更好进行入队,添加构造函数node(int xx,int yy,int stepp,bool flagg){x=xx;y=yy;step=stepp;flag=flagg;   }
};
queue<node> q;int bfs(int x,int y)
{//一开始在起点时步数为0,并且手里没有钥匙,可以用false代表,也可以用0代表 q.push(node(x,y,0,false));v[x][y][0]=true; //第三维为0,因为时没有钥匙的状态,true代表访问,也可以用1代表 while(!q.empty()){//获取队首元素node now = q.front();q.pop();  //获取队首元素之后,就可以实现出队了,因为队首元素已经保存在now了,不会丢失//队首可扩展的点进行入队for(int i=0;i<4;i++){int tx=now.x+dir[i][0];int ty=now.y+dir[i][1];if(tx>=0&&tx<n&&ty>=0&&ty<m&&a[tx][ty]!='#'&&!v[tx][ty][now.flag]){v[tx][ty][now.flag]=1;if(a[tx][ty]=='P'){q.push(node(tx,ty,now.step+1,true));}else if(a[tx][ty]=='T'&&now.flag==1){return now.step+1;}else{q.push(node(tx,ty,now.step+1,now.flag));  //此点是否带了钥匙受队首元素的影响 }}}}
}int main()
{cin>>n>>m;  //n行m列for(int i=0;i<n;i++){cin>>a[i]; }    //寻找起点S的坐标位置for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(a[i][j]=='S')  {sx=i;sy=j;}} } //从起点出发,进行bfscout<<bfs(sx,sy); return 0;
}

代码实现2:

#include<bits/stdc++.h>
using namespace std;
/*
算出拿钥匙的路+回家的路的最短距离
思路1:求出钥匙到家到起点的两个值,将二者相加即可,求出最小的即为 最短距离 思路2:较难理解,可以利用excel表格画图
提示:同学们要注意,老师上课讲得一些题目都非常经典,那么都是取自于历年的一些真题,上课以题目为主
通过题目的方式去讲解相关的知识,所以同学们一定要学会相关的一些写法以及相关的一些思路 样例1:
8 10
P.####.#P#
..#..#...#
..#T##.#.#
..........
..##.#####
..........
#####...##
###....S##样例1答案:
21*/
const int N = 2005;
int n,m; //地图大小
int sx,sy,ex,ey; //起点和终点
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; //右 下 左 上
char a[N][N]; //地图是一个二维数组,行列不超过2000
bool v[N][N][2]; //一般搜索都会涉及到一个访问数组,不管是DFS还是BFS,都需要将访问过的点设置为已经访问
// 要特别注意v这个数组的含义,第三维用来标记蒜头君走过某个点的时候是否已经带了钥匙
struct node
{int x,y; //坐标int step;bool flag; //钥匙的状态,1代表有钥匙,0代表没有钥匙  //为了更好进行入队,添加构造函数node(int xx,int yy,int stepp,bool flagg){x=xx;y=yy;step=stepp;flag=flagg;   }
};
queue<node> q;void bfs(int x,int y)
{//一开始在起点时步数为0,并且手里没有钥匙,可以用false代表,也可以用0代表 q.push(node(x,y,0,false));v[x][y][0]=true; //第三维为0,因为时没有钥匙的状态,true代表访问,也可以用1代表 while(!q.empty()){//获取队首元素node now = q.front();q.pop();  //获取队首元素之后,就可以实现出队了,因为队首元素已经保存在now了,不会丢失//判定是否抵达终点if(a[now.x][now.y]=='T'){if(now.flag){cout<<now.step<<endl;break;        }continue; //到达终点但是没有钥匙,这个点无需再扩展 } //队首可扩展的点进行入队for(int i=0;i<4;i++){int tx=now.x+dir[i][0];int ty=now.y+dir[i][1];if(tx>=0&&tx<n&&ty>=0&&ty<m&&a[tx][ty]!='#'&&!v[tx][ty][now.flag]){v[tx][ty][now.flag]=1;if(a[tx][ty]=='P'){q.push(node(tx,ty,now.step+1,true));}else{q.push(node(tx,ty,now.step+1,now.flag));  //此点是否带了钥匙受队首元素的影响 }}}}
}int main()
{cin>>n>>m;  //n行m列for(int i=0;i<n;i++){cin>>a[i]; }    //寻找起点S的坐标位置for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(a[i][j]=='S')  {sx=i;sy=j;}} } //从起点出发,进行bfsbfs(sx,sy); return 0;
}

总结: 如果对以上的讲解内容或者代码有任何疑问的话,可在评论区里回复,我会及时答复,也可以私信我,将你添加到算法群聊中,一起来学习成长。这一期的算法课堂到这里就结束啦!

BFS应用之蒜头君回家—C说算法系列相关推荐

  1. 计蒜客-蒜头君回家(bfs)

    蒜头君要回家,但是他家的钥匙在他的朋友花椰妹手里,他要先从花椰妹手里取得钥匙才能回到家.花椰妹告诉他:"你家的钥匙被我复制了很多个,分别放在不同的地方." 蒜头君希望能尽快回到家中 ...

  2. 计蒜客--蒜头君回家

    蒜头君要回家,但是他家的钥匙在他的朋友花椰妹手里,他要先从花椰妹手里取得钥匙才能回到家.花椰妹告诉他:"你家的钥匙被我复制了很多个,分别放在不同的地方." 蒜头君希望能尽快回到家中 ...

  3. 【宽度优先搜索】计蒜客:蒜头君回家(带条件的BFS)

    8 10 P.####.#P# ..#..#...# ..#T##.#.# .......... ..##.##### .......... #####...## ###....S## 21 求最短步 ...

  4. 蒜头君救人 状压DP

    蒜头君救人 题目描述 蒜头君是一个乐于助人的好孩子,这天他所在的乡村发生了洪水,有多名村民被困于孤岛上,于是蒜头君决定去背他们离开困境,假设蒜头君所在的村子是 n×m n×m 的网格,网格中.号代表平 ...

  5. 61计蒜客 动态规划基础 蒜头君的城堡之旅

    题目: 蒜国地域是一个 n 行 m 列的矩阵,下标均从 1 开始.蒜国有个美丽的城堡,在坐标 (n,m) 上,蒜头君在坐标 (1,1) 的位置上.蒜头君打算出发去城堡游玩,游玩结束后返回到起点.在出发 ...

  6. 算法学习之路|蒜头君的新游戏1

    工作空闲之余,蒜头君经常带着同事们做游戏,最近蒜头君发明了一个好玩的新游戏:nn 位同事围成一个圈,同事 A 手里拿着一个兔妮妮的娃娃.蒜头君喊游戏开始,每位手里拿着娃娃的同事可以选择将娃娃传给左边或 ...

  7. 栈和递归---手动实现一个栈和蒜头君吃桃

    栈 push--压入 pop--弹出 特点:先进后出 一.手动实现一个栈 封装数据结构--栈写成class或struct,将当前栈的数据和对当前栈的操作都放在里面 定义一个结构体Stack,规定最大存 ...

  8. 试题11 蒜头君的随机数(排序和去重)

    题目: 蒜头君想在学校里请一些同学一起做一项问卷调查,为确保实验客观性,他先用计算机生成n(i<=n<=100)个1到1000之间的随机整数,对于其中重复的数字,只保留一个,把其余相同的数 ...

  9. 计蒜客--蒜头君的新游戏

    1000ms  131072K 工作空闲之余,蒜头君经常带着同事们做游戏,最近蒜头君发明了一个好玩的新游戏:n 位同事围成一个圈,同事 A 手里拿着一个兔妮妮的娃娃.蒜头君喊游戏开始,每位手里拿着娃娃 ...

最新文章

  1. 排序学习之---快速排序
  2. CALayer的基本操作
  3. 关于计算机组装的作文,电脑小白组装电脑,能写出这样的配置,在下佩服!
  4. php常见的几种排序以及二分法查找
  5. oracle创建定时任务
  6. Flutter 中获取地理位置[Flutter专题61]
  7. Mask-RCNN中的ROIAlign, ROIPooling及ROIWarp对比
  8. 让Apache支持Wap网站
  9. 【2018icpc宁夏邀请赛现场赛】【Gym - 102222A】Maximum Element In A Stack(动态的栈中查找最大元素)
  10. Javascript——进阶(事件、数组操作、字符串操作、定时器)
  11. 怎样才能成为优秀的前端工程师
  12. appium启动APP配置参数:
  13. case when then else_SQL 优化大神玩转 MySQL函数系列_case_when 的坑
  14. Python:第六次全国人口普查数据分析及可视化(pandas、matplotlib)
  15. 软件工程(敏捷过程和极限编程)
  16. Miracle密码算法开源库(十三)分析 :mrflsh4.c
  17. CC00072.pbpositions——|HadoopPB级数仓.V01|——|PB数仓.v01|Griffin数据质量监控工具|概述|
  18. 使用racoon setkey搭建IPsec环境
  19. (净化anaconda)conda install 中途打断出现ERROR REPORT
  20. 羊吃草--二分图匹配

热门文章

  1. 损失函数Loss Function
  2. JAVA集合案例之使用Iterator迭代遍历输出企鹅信息升级版
  3. linux打的war包在linux上无法运行(一个奇葩事件,spring无法读取Properties)
  4. C#语言实例源码系列-实现自己的进程管理器
  5. table设置colspan属性,列宽显示错位解决方法
  6. 故宫屋顶的小动物干嘛用
  7. 基于改进YOLOv5的斑马线和汽车过线行为检测
  8. 2021年QS世界大学排名香港科大荣居全球第27名,港校逆势崛起创历史新高!
  9. 记账方法,如何记录借款信息,并将支出记录标记上颜色
  10. 3*3矩阵乘法 c语言,c语言矩阵相乘