当前位置: 首页 > news >正文

C++使用BFS求解最短路径

C++使用BFS求解最短路径

  • 一、BFS原理简介
  • 二、BFS应用——蓝桥杯穿越雷区题目
    • 2.1题目概述
    • 2.2实现代码
    • 2.3蓝桥杯平台提交结果
  • 三、BFS应用——蓝桥杯迷宫与陷阱题目
    • 3.1题目概述
    • 3.2实现代码
    • 3.3蓝桥杯平台提交结果

一、BFS原理简介

BFS(Breadth First Search),中文名为广度优先搜索,是一种遍历搜索算法,其特点是按照距离起始点的路径长度进行搜索,即先将第一层的节点搜索完毕,再搜索下一层

与之对应的是DFS(Depth First Search)深度优先搜索,优先对深度方向进行搜索,先搜索到最底层,然后再返回进行另一条路径的深度搜索

由于BFS是按层搜索的,到达每一层节点所走的路径长度一定大于到达上一层节点的路径长度,因此可以用于最短路径的求解,但如果节点与节点间存在权重,例如层级间的路径长度不相同,那么BFS就不一定能够找到最短路径
在这里插入图片描述

二、BFS应用——蓝桥杯穿越雷区题目

2.1题目概述

穿越雷区题目为蓝桥杯2015年国赛程序设计题,题目大致要求如下:

  1. 在一个给定方阵中,要求求出从输入的A点到B点的最短路径;
  2. 行走过程中有一个限制,需要交替走过“+”、“-”标记的节点。

此题是一个典型的考察BFS算法的题目,只是题目中增加了节点搜索的限制,即搜索节点的下一个节点时必须搜索满足条件的节点,只需对算法进行略微修改就可求解题目

2.2实现代码

代码使用C++编写,如下:

#include<iostream>
#include<algorithm>
#include<queue>
#include<chrono>
using namespace std;
const int N = 110;
int n, a[N][N],vis[N][N];
int nt[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
pair<int, int>st, ed;
struct node
{
	int x, y, cnt;
};

int bfs(int x, int y)
{
	queue<node> que;
	que.push(node{x,y,0});
	vis[x][y]=1;
	while(!que.empty())
	{
		node u=que.front();
		if(u.x==ed.first && u.y==ed.second) return u.cnt;
		que.pop();
		int x=u.x, y=u.y;
		for(int i =0; i < 4; i++)
		{
			int tx=x+nt[i][0],ty=y+nt[i][1];
			if(tx < 0 || tx > n-1 || ty < 0 || ty > n-1 || vis[tx][ty] || a[tx][ty]==a[x][y]) continue;
			que.push(node{tx,ty,u.cnt+1});
			vis[tx][ty]=1;
		}
	}
	return -1;
}

int main()
{
	cin>>n;
	for(int i =0; i < n; i++)
	{
		for(int j = 0; j < n; j++)
		{
			char x;
			cin>>x;
			if(x=='A') st.first=i,st.second=j,a[i][j]=-1;
			if(x=='B') ed.first=i,ed.second=j,a[i][j]=-1;
			if(x=='+') a[i][j]=1;
			if(x=='-') a[i][j]=0;
		}
	}
	int x=st.first,y=st.second;
	cout<<bfs(x,y)<<endl;
	return 0;
}

BFS算法通常使用队列结构进行节点存储,由于队列先进先出的特性,会先输出同级的节点,然后添加这一节点的下层相邻节点。本题在添加下层节点时有两个限制:

  1. 节点坐标不能超出方阵范围
  2. 下层相邻节点的标记字符即“+”、“-”不能与当前节点相同
  3. 节点只能访问一次,因为如果第二次添加此节点,那么说明走了回路,即便能够走到终点,路径总长一定大于第一次添加此节点时的路径长度。

2.3蓝桥杯平台提交结果

为测试代码正确性,可以在蓝桥杯的刷题平台提交代码,网址为https://www.lanqiao.cn/problems/?first_category_id=1。

穿越雷区题目编号为141,如下图所示:
在这里插入图片描述
代码执行结果如下图,可获得满分
在这里插入图片描述
在提交代码测试过程中发现,无法正常输出测试输入数据,例如本题第一个测试用例方阵长宽为10,则应有100个字符,但在代码中尝试输出这100个字符时,只能输出60多个字符,原因暂且未知

三、BFS应用——蓝桥杯迷宫与陷阱题目

3.1题目概述

题目整体设置的框架与穿越雷区类似,不同之处如下:

  1. 起点固定为左上角,终点固定为右下角;
  2. 某些节点为墙壁,无法通过;
  3. 某些节点为无敌节点,通过此节点获得无敌状态,在接下来的K步内保持这一状态;
  4. 某些节点为陷阱节点,只有无敌状态才能通过。

本题的关键之处在于有一个无敌状态的设定,该设定使得在某些情况下节点可以重复访问

  1. 第二次经过某节点的无敌状态的剩余步数大于第一次经过时的步数,那么此时可以重复访问;
  2. 因为无敌状态的剩余步数的变大,虽然较第一次增加了一小段路径长度,但有可能穿过陷阱实现总路径长度更小,到达终点。

3.2实现代码

C++代码如下:

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

const int N=1100;
int a[N][N], vis[N][N], sta[N][N];
int nxt[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct node
{
	int x,y,cnt,status;
};

int bfs(int x, int y, int cnt, int status, int n, int k)
{
	queue<node> que;
	que.push(node{x,y,cnt,status});
	vis[x][y]=1;
	while(!que.empty())
	{
		node front=que.front();
		if(front.x == n - 1 && front.y == n - 1) return front.cnt;
		que.pop();
		for(int i = 0; i < 4; i++)
		{
			int nx = front.x + nxt[i][0], ny = front.y + nxt[i][1];
			int nsta = max(0, front.status - 1);
			if(nx < 0 || nx > n - 1 || ny < 0 || ny > n - 1 || a[nx][ny] == 3) continue;
			if(a[nx][ny] == 2 && nsta == 0) continue;
			if(a[nx][ny] == 1 && !vis[nx][ny])
			{
				nsta = k + 1;
				que.push(node{nx,ny,front.cnt + 1,nsta});
				vis[nx][ny] = 1;
				sta[nx][ny] = nsta;
				continue;
			}
			if(vis[nx][ny])
			{
				if(sta[nx][ny] >= nsta) continue;
				que.push(node{nx,ny,front.cnt + 1,nsta});
				sta[nx][ny] = nsta;
			}
			else
			{
				que.push(node{nx,ny,front.cnt + 1,nsta});
				vis[nx][ny] = 1;
				sta[nx][ny] = nsta;
			}
		}
	}
	return -1;
}

int main()
{
	int n, k;
	cin>>n>>k;
	for(int i = 0; i < n; i++)
	{
		for(int j = 0; j < n; j++)
		{
			char x;
			cin>>x;
			if(x == '%') a[i][j]=1;
			if(x == 'x') a[i][j]=2;
			if(x == '#') a[i][j]=3;
		}
	}
	cout<<bfs(0, 0, 0, 0, n, k)<<endl;
	return 0;
}

代码中关键之处如下:

  1. 无敌节点只能访问一次,因为到达无敌节点后的无敌剩余步数都相同,只会导致路径长度的增加;
  2. 单独建立一个sta二维数组,存储无敌剩余步数,用于比较判断是否二次添加节点;
  3. 获得无敌状态后保持K步,那么到达无敌节点时的无敌状态剩余步数应是K+1,这样才能保持K步;

3.3蓝桥杯平台提交结果

迷宫与陷阱题目编号为229,如下图所示:
在这里插入图片描述
代码执行结果如下图,可获得满分
在这里插入图片描述

相关文章:

  • NS3学习——运行自定义拥塞控制算法步骤
  • 动态规划中固定倒数第二个数与倒数第一个数的区别与应用场景分析 —— 从最长等差数列问题到统计等差数列个数的填表策略对比
  • 关于前后端整合和打包成exe文件的个人的总结和思考
  • 数据集构建与训练前准备
  • VIC模型有哪些优势?适用哪些范围?基于QGIS的VIC模型建模;未来气候变化模型预测;基于R语言VIC参数率定和优化
  • 【Academy】Web 缓存欺骗 ------ Web cache deception
  • 如何实现wordpress搜索自字义字段内容
  • 分享最佳ChatGPT替代11个方案(2025)
  • 计算机组成原理(第六章 总线)
  • 关于在electron(Nodejs)中使用 Napi 的简单记录
  • 内容中台的核心架构是什么?
  • 【在校课堂笔记】Python 第 3 节课 总结
  • FlinkSQL源码笔记
  • ~(取反)在算法竞赛中的常见用法和注意事项
  • 线程管理操作
  • 【CSS3】金丹篇
  • 3.3.5 VO-O语法- 高级语法
  • 大语言模型中Token的输出过程
  • vue+dhtmlx-gantt 实现甘特图-快速入门【甘特图】
  • Python 编程题 第八节:字符串变形、压缩字符串、三个数的最大乘积、判定字符是否唯一、IP地址转换
  • 歼-10CE首战大放异彩,聊聊中国战机海外实战的那些事儿
  • 美发布“金穹”导弹防御系统发展规划
  • 广西隆林发生一起山洪灾害,致4人遇难
  • 林园:茅台一直是稀缺资源,股东比较有信仰,依旧看好白酒市场
  • 北美票房|华纳又赢了,《死神来了6》开画远超预期
  • 莱布雷希特专栏:古典乐坛边缘人