周六900C++班级2022-11-5 广搜
广度优先搜索
第一步
7588: 农夫抓牛
农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0<=N<=100000),牛位于点K(0<=K<=100000)。农夫有两种移动方式:
1、从X移动到X-1或X+1,每次移动花费一分钟
2、从X移动到2*X,每次移动花费一分钟
假设牛没有意识到农夫的行动,站在原地不动。农夫最少要花多少时间才能抓住牛?

#include<bits/stdc++.h> using namespace std; struct node{ int x,y,step; }; node q[100005]; int nex[3] = {-1,1,2}; int vis[100005]; //标记数组 int n,k,f,ans; //f终点的标记 void bfs() { int head = 1,tail = 1; q[tail].x = n; q[tail].step = 0; tail++; //扩充队尾 vis[n] = 1; //标记起点 while(head<tail) { for(int i=0;i<3;i++) { int tx; if(i<2) tx = nex[i]+q[head].x; //方向数组+队首head的位置 else tx = nex[i]*q[head].x; if(tx>100000 || tx<0) continue;//越界判断 if(vis[tx]==0) //vis数组里没标记过,那么就让tx加入队尾 { vis[tx] = 1; //标记tx q[tail].x = tx; q[tail].step = q[head].step+1; tail++; //扩充队列长度 } if(tx==k){ //找到终点 f = 1; ans = q[tail-1].step; break; } } if(f)break; head++; //队首往后一位 } } int main() { cin>>n>>k; //输入起点终点 bfs(); //寻找最短路 cout<<ans; //输出最短路长 return 0; }
6878: 青蛙跳
一只青蛙沿着一条多个石头格子铺成的路去前方寻找虫子,某些格子有障碍物不能踩,但青蛙能够越过。已知青蛙一次最多能越过d个格子(如从第一个格子到第三个格子,越过了1个格子)。
路的一端是青蛙当前位置,另一端则是虫子所在位置(两个格子都没有障碍物),问青蛙最少需要跳几次才能到达虫子所在格子。

#include<bits/stdc++.h> using namespace std; struct node{ int x,y,step; }; node q[100005]; int nex[3] = {-1,1,2}; int vis[100005]; //标记数组 int n,k,f,ans; //f终点的标记 string s; void bfs() { int head = 1,tail = 1; q[tail].x = 0; q[tail].step = 0; tail++; //扩充队尾 vis[0] = 1; //标记起点 while(head<tail) { for(int i=1;i<=k+1;i++) { int tx = q[head].x + i; if(tx>=n) continue;//越界判断 if(vis[tx]==0 && s[tx]!='X') //vis数组里没标记过,那么就让tx加入队尾 { vis[tx] = 1; //标记tx q[tail].x = tx; q[tail].step = q[head].step+1; tail++; //扩充队列长度 } if(tx==n-1){ //找到终点 f = 1; ans = q[tail-1].step; break; } } if(f)break; head++; //队首往后一位 } } int main() { cin>>n>>k; cin>>s; bfs(); //寻找最短路 cout<<ans; //输出最短路长 return 0; }
6314: 走迷宫
一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。
给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。

#include<bits/stdc++.h> using namespace std; struct node{ int step,x,y; }; node q[1605]; //队列 char a[50][50]; //地图 int vis[50][50]; //标记数组 int nex[4][2] = {{0,1},{1,0},{0,-1},{-1,0}}; //右下左上四个方向 int n,m,f,ans; //n行,m列,f终点标记 ,ans记录答案的步长 void bfs() { int head = 1,tail = 1; //定义队首head,队尾tail q[tail].x = 1; q[tail].y = 1; q[tail].step = 1; //14-16行 => 起点入队 tail++; //队尾扩充 vis[1][1] = 1; //给起点标记上 while(head<tail) { for(int i=0;i<4;i++) //循环4个方向 { int tx = q[head].x + nex[i][0]; //下一步的坐标tx = 队首head.x + 第i个方向的x增量nex[i][0] int ty = q[head].y + nex[i][1]; if(tx<1 || tx>n || ty<1 || ty>m)continue; //越界判断 if(a[tx][ty]=='.' && vis[tx][ty] == 0) //判断tx,ty在地图a上是否可走 且 在标记数组vis上没标记过 { vis[tx][ty] = 1; //将下一步(tx,ty)在vis数组上标记 q[tail].x = tx; q[tail].y = ty; q[tail].step = q[head].step + 1; //队尾步长 = 队首步长 + 1 tail++; //扩充队尾 } if(tx==n && ty==m) //判断下一步坐标tx,ty是否已经是终点 { f = 1; //标记终点已找到 ans = q[tail-1].step; //到终点的步长 = 队尾-1的步长 break; } } if(f==1)break; head++; } } int main() { cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; //输入第i行第j列的数据 bfs(); cout<<ans; return 0; }
7587:仙岛求药
少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶。叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处。迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全。现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他。现在要求你来帮助他实现这个目标。
下图 显示了一个迷阵的样例及李逍遥找到仙药的路线

#include<bits/stdc++.h> using namespace std; struct node{ int step,x,y; }; node q[1605]; //队列 char a[50][50]; //地图 int vis[50][50]; //标记数组 int nex[4][2] = {{0,1},{1,0},{0,-1},{-1,0}}; //右下左上四个方向 int n,m,f,ans; //n行,m列,f终点标记 ,ans记录答案的步长 int sx,sy,ex,ey; void bfs() { int head = 1,tail = 1; //定义队首head,队尾tail q[tail].x = sx; q[tail].y = sy; q[tail].step = 0; //14-16行 => 起点入队 tail++; //队尾扩充 vis[sx][sy] = 1; //给起点标记上 while(head<tail) { for(int i=0;i<4;i++) //循环4个方向 { int tx = q[head].x + nex[i][0]; //下一步的坐标tx = 队首head.x + 第i个方向的x增量nex[i][0] int ty = q[head].y + nex[i][1]; if(tx<1 || tx>n || ty<1 || ty>m)continue; //越界判断 if(a[tx][ty]!='#' && vis[tx][ty] == 0) //判断tx,ty在地图a上是否可走 且 在标记数组vis上没标记过 { vis[tx][ty] = 1; //将下一步(tx,ty)在vis数组上标记 q[tail].x = tx; q[tail].y = ty; q[tail].step = q[head].step + 1; //队尾步长 = 队首步长 + 1 tail++; //扩充队尾 } if(tx==ex && ty==ey) //判断下一步坐标tx,ty是否已经是终点 { f = 1; //标记终点已找到 ans = q[tail-1].step; //到终点的步长 = 队尾-1的步长 break; } } if(f==1)break; head++; } } int main() { while(cin>>n>>m) { if(n==0&&m==0)break; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) { cin>>a[i][j]; if(a[i][j]=='@'){ //起点 sx = i;sy = j; } if(a[i][j]=='*'){ //终点 ex = i;ey = j; } } } memset(vis,0,sizeof(vis)); //将vis数组的全部数据初始化为0 ans = -1; // f = 0; bfs(); cout<<ans<<endl; } return 0; }
5761: 最少步数
在各种棋中,棋子的走法总是一定的,如中国象棋中马走“日”。有一位小学生就想如果马能有两种走法将增加其趣味性,因此,他规定马既能按“日”走,也能如象一样走“田”字。他的同桌平时喜欢下围棋,知道这件事后觉得很有趣,就想试一试,在一个(100×100)的围棋盘上任选两点A、B,A点放上黑子,B点放上白子,代表两匹马。棋子可以按“日”字走,也可以按“田”字走,俩人一个走黑马,一个走白马。谁用最少的步数走到左上角坐标为(1,1)的点时,谁获胜。现在他请你帮忙,给你A、B两点的坐标,想知道两个位置到(1,1)点可能的最少步数。

#include<bits/stdc++.h> using namespace std; struct node{ int step,x,y; }; node q[10001]; //队列 int vis[105][105]; //标记数组 int nex[12][2]={{1,2},{2,1},{1,-2},{-2,1},{-1,2},{2,-1},{-1,-2},{-2,-1},{2,2},{2,-2},{-2,2},{-2,-2}}; //右下左上四个方向 int n,m,f,ans; //n行,m列,f终点标记 ,ans记录答案的步长 int sx,sy,ex,ey; void bfs() { int head = 1,tail = 1; //定义队首head,队尾tail q[tail].x = sx; q[tail].y = sy; q[tail].step = 0; //14-16行 => 起点入队 tail++; //队尾扩充 vis[sx][sy] = 1; //给起点标记上 while(head<tail) { for(int i=0;i<12;i++) //循环4个方向 { int tx = q[head].x + nex[i][0]; //下一步的坐标tx = 队首head.x + 第i个方向的x增量nex[i][0] int ty = q[head].y + nex[i][1]; if(tx<1 || tx>100 || ty<1 || ty>100)continue; //越界判断 if(vis[tx][ty] == 0) //判断tx,ty在地图a上是否可走 且 在标记数组vis上没标记过 { vis[tx][ty] = 1; //将下一步(tx,ty)在vis数组上标记 q[tail].x = tx; q[tail].y = ty; q[tail].step = q[head].step + 1; //队尾步长 = 队首步长 + 1 tail++; //扩充队尾 } if(tx==ex && ty==ey) //判断下一步坐标tx,ty是否已经是终点 { f = 1; //标记终点已找到 ans = q[tail-1].step; //到终点的步长 = 队尾-1的步长 break; } } if(f==1)break; head++; } } int main() { for(int u=1;u<=2;u++) { cin>>sx>>sy; memset(vis,0,sizeof(vis)); //初始化标记数组vis全部为0 f = 0; //终点标记 f = 0 ex = 1;ey = 1; bfs(); cout<<ans<<endl; } return 0; }
——————————————————————————————————————————————————
总结:
广搜相较于深搜来说更加的模板化其实应用范围也并不广,在蓝桥杯和CSP的考试中更多是考察类似于深搜的题目,但是广搜是在NOI大纲中的,作为搜寻最短路的方法,是要求大家掌握的,而且广搜是与图论最短路章节息息相关的内容,在未来考到的频次可能也会增加 所以想学好广搜需要注意的点是理清广搜这一过程中队列的思想,对于当前的队首head,能加入队尾tail的一定是跟队首head相连通的点才能加入队尾;记住循环是head<tail。细心的同学一定能看出来,BFS中最开始的初始化结束后tail会++;当确认下一点tx,ty可走时将tx,ty加入队尾tail后tail也会++,而队首head往后移是发生在head的方向循环结束后,代表这个队首head已经把所有方向都遍历完的情况下,才会让head++;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现