多源BFS——AcWing 173. 矩阵距离

多源BFS

定义

多源BFS(多源广度优先搜索)是一种图遍历算法,它是标准BFS(广度优先搜索)的扩展,主要用于解决具有多个起始节点的最短路径问题。在多源BFS中,不是从单一源点开始搜索整个图,而是同时从多个源点出发,寻找这些源点到图中所有其他节点的最短路径。这种方法特别适用于边权都为1的情况,如在网格图中计算点到点的最短曼哈顿距离或欧几里得距离。

多源BFS通过初始化一个队列,将所有源节点放入队列中开始。算法执行标准的BFS过程,但每次从队列中取出一个节点进行扩展时,会检查这个节点是否已经被访问过,以避免重复处理。每个节点的距离是从其最近的源节点测量的,并且维护一个数据结构(如二维数组或字典)来存储每个节点的最短距离。

运用情况

  1. 网格图中的最短距离:例如,在一个由0和1组成的矩阵中,0表示可以通过的路径,1表示障碍,多源BFS可以用来找出每个位置到最近的0的距离。
  2. 社交网络中的影响力传播:在社交网络中,如果要计算一个人发布的信息经过多个人传播后能影响到的最远节点,可以将初始发布者作为多个源点进行多源BFS。
  3. 游戏地图探索:在某些游戏中,玩家可能从多个入口进入迷宫或地图,多源BFS可以用来快速找到各个入口到所有可到达点的最短路径。

注意事项

  1. 标记已访问:确保每个节点只被访问一次,避免循环和重复计算。
  2. 初始化源节点的距离:源节点到自身的距离应初始化为0,非源节点的距离可以初始化为无穷大或一个表示未访问的特殊值。
  3. 使用合适的数据结构:为了高效地管理待访问节点和已访问节点的信息,选择适当的数据结构(如队列、集合、字典等)至关重要。
  4. 剪枝:在某些情况下,可以通过剪枝策略提前终止不必要的搜索路径,以优化性能。

解题思路

  1. 初始化:将所有源节点加入队列,并初始化所有节点的距离(源节点为0,其余为无穷大或特殊值)。
  2. 遍历:执行BFS循环,直到队列为空。每次循环从队列头部取出一个节点,检查其邻居节点。
  3. 更新距离:对于当前节点的每个未访问邻居,计算从源点通过当前节点到达该邻居的距离,如果这个距离比已知的最短距离更短,则更新该邻居的距离,并将其加入队列。
  4. 终止条件:当队列为空时,所有可达节点的最短距离都已被计算。

AcWing 173. 矩阵距离

题目描述

173. 矩阵距离 - AcWing题库

运行代码

#include <iostream>
#include <cstring>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;int n, m;
char g[N][N];
PII q[N * N];
int dist[N][N];void bfs()
{int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};int hh = 0, tt = -1;for(int i = 1; i <= n; i ++ )for(int j = 1; j <= m; j ++ )if(g[i][j] == '1'){dist[i][j] = 0;q[++ tt] = {i, j};}while(hh <= tt){PII t = q[hh ++];for(int i  =0; i < 4; i ++ ){int a = t.x + dx[i], b = t.y + dy[i];if(a <= 0 || a > n || b <= 0 || b > m) continue;if(dist[a][b] != -1) continue;dist[a][b] = dist[t.x][t.y] + 1;q[++ tt] = {a, b};}}
}int main()
{memset(dist, -1, sizeof dist);cin >> n >> m;for(int i = 0; i < n; i ++ ) cin >> g[i + 1] + 1;bfs();for(int i = 1; i <= n; i ++ ){for(int j = 1; j <= m; j ++ ) cout << dist[i][j] << ' ';cout << endl;}return 0;
}

代码思路

  1. 数据结构定义与常量设置:
    • 使用PII(pair<int, int>)类型来表示二维网格中的坐标。
    • dx[]dy[]数组分别存储了四个基本方向的横纵坐标增量,用于遍历邻居节点。
    • N定义了网格的最大尺寸,这里设为1010。
  2. 变量初始化:
    • 读取网格的行数n和列数m
    • 通过cin读取二维字符矩阵g,'1'代表源点,假设其他位置默认为'0'(虽然代码中未直接处理'0'的情况,但逻辑上是这样理解的)。
    • 使用memset将距离矩阵dist初始化为-1,表示尚未访问。
  3. 广度优先搜索(BFS)实现:
    • 遍历矩阵,将所有值为'1'的点作为起点,其距离设为0,并将它们的坐标存入队列q
    • 开始BFS过程:从队列中取出一个点,检查它的四个邻居(上、下、左、右),若邻居坐标在矩阵范围内且尚未访问过(dist[a][b] == -1),则更新邻居的距离为当前点的距离加1,并将邻居加入队列。这一步确保了从所有源点出发,逐步扩展到整个网格,同时计算每个点到最近源点的距离。
  4. 结果输出:遍历二维数组dist,按照网格的行列顺序输出每个位置的最短距离,每个数字后面跟一个空格,每行结束后换行打印。

改进思路

  1. 代码注释和文档:虽然代码逻辑相对清晰,但增加更多的注释说明每个主要步骤的作用、边界条件处理的原因以及算法的核心思想,可以提高代码的可读性和可维护性。

  2. 异常处理和输入验证:在实际应用中,增加对输入数据的校验是非常重要的。比如,可以检查nm的范围是否合理,以及输入矩阵是否符合预期格式,以避免运行时错误。

  3. 空间优化:虽然本代码的空间复杂度已经是线性的,但考虑到在极端情况下(如矩阵几乎全为'1')队列q可能会占用大量内存,可以考虑在BFS过程中直接从当前层节点扩展到下一层,而不是将所有节点都存入队列,这样可以减少队列的最大容量需求。

  4. 并行化处理:如果面对的是非常大的矩阵,可以考虑使用多线程或多进程并行执行BFS,每个线程负责处理一部分源点或区域,以加速计算过程。但这需要引入线程同步机制来避免数据竞争问题。

  5. 使用STL容器:虽然数组在性能上通常有优势,但考虑到代码的灵活性和易读性,可以考虑使用STL容器如vector<vector<int>>来代替二维数组distg,特别是当矩阵大小不固定或需要动态调整时。

  6. 常量表达式和类型别名:可以进一步利用C++特性,比如将方向数组定义为constexpr以提高效率,或者使用using Grid = vector<vector<char>>;来定义一个类型别名,使得代码更加现代和清晰。

  7. 宏替换为内联函数或常量:尽管宏定义在本代码中简化了访问pair成员的操作,但使用内联函数或const变量可以提供更好的类型安全性和调试信息。

改进代码

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <limits>
using namespace std;// 方向向量,表示上下左右四个方向
constexpr array<pair<int, int>, 4> directions = {{{-1, 0}, {0, 1}, {1, 0}, {0, -1}}};// 使用vector替代数组以支持动态大小和更现代的C++实践
using Grid = vector<vector<char>>;
using pii = pair<int, int>; // 简化pair<int, int>的写法int n, m;
Grid g;
vector<vector<int>> dist;
queue<pii> q;// BFS函数,计算每个点到最近'1'的距离
void bfs() {while (!q.empty()) {auto [cx, cy] = q.front(); q.pop();for (const auto& [dx, dy] : directions) {int nx = cx + dx, ny = cy + dy;if (nx >= 0 && nx < n && ny >= 0 && ny < m && g[nx][ny] != '1' && dist[nx][ny] == -1) {dist[nx][ny] = dist[cx][cy] + 1;q.push({nx, ny});}}}
}int main() {cin >> n >> m;g.resize(n, vector<char>(m));dist.assign(n, vector<int>(m, -1)); // 初始化距离矩阵为-1// 读取矩阵for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {cin >> g[i][j];// 将所有的'1'点加入队列,并初始化其距离为0if (g[i][j] == '1') {dist[i][j] = 0;q.push({i, j});}}}bfs(); // 执行BFS// 输出结果for (const auto& row : dist) {for (int d : row) {cout << (d != -1 ? d : 0) << ' '; // 如果是-1,表示不可达,输出0}cout << endl;}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/39678.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么把webp格式转换成jpg?5个图片格式转换方法全面解析(2024最新)

webp 图片常用于网站&#xff0c;可显著改善页面的浏览和加载体验。然而&#xff0c;许多设备&#xff08;如苹果手机设备、安卓手机等&#xff09;不支持webp文件。在这些设备上查看webp文件时&#xff0c;最佳做法是将其转换为其他常见格式&#xff0c;如jpg或 png。Windows电…

2024上海大学生程序设计竞赛I-六元组计数原根知识详解

以前基本没有了解原根相关的一块内容&#xff0c;最近正好碰到了这个题&#xff0c;于是写一篇博客记录一下。 这道题的总体思路就是比较明显&#xff0c;就是先算出 a p x a^px apx对于每个 x x x的解的个数&#xff0c;然后NTT算一下即可。 先来讲一下怎么求欧拉函数 ϕ ( …

前端FCP指标优化

优化前 第三方依赖按需引入之后&#xff0c;打包的总体积减小到初始值的55%&#xff0c;但是依然存在很大的js文件&#xff0c;需要继续优化 chunk-vendors.js进行分包之后 截图 compression-webpack-plugin压缩之后 截图

大学新生人工智能学习路线规划

1. 引言 七月来临&#xff0c;各省高考分数已揭榜完成。而高考的完结并不意味着学习的结束&#xff0c;而是新旅程的开始。对于有志于踏入IT领域的高考少年们&#xff0c;这个假期是开启探索IT世界的绝佳时机。作为该领域的前行者和经验前辈&#xff0c;我愿意为准新生们提供一…

剪映 v5.5 Pro Vip解锁版:使用指南与注意事项

摘要&#xff1a;本文介绍了剪映Pro VIP解锁版的使用方法&#xff0c;包括安装、测试和使用VIP素材的步骤&#xff0c;以及如何避免误报和保持解锁状态的建议。 正文&#xff1a; 剪映Pro是一款广受欢迎的视频编辑软件&#xff0c;提供了丰富的视频编辑功能和大量高质量的素材…

发送微信消息和文件

参考&#xff1a;https://www.bilibili.com/video/BV1S84y1m7xd 安装&#xff1a; pip install PyOfficeRobotimport PyOfficeRobotPyOfficeRobot.chat.send_message(who"文件传输助手", message"你好&#xff0c;我是PyOfficeRobot&#xff0c;有什么可以帮助…

RabbitMQ中java实现队列和交换机的声明

java实现队列和交换机的声明 在之前我们都是基于RabbitMQ控制台来创建队列、交换机。但是在实际开发时&#xff0c;队列和交换机是程序员定义的&#xff0c;将来项目上线&#xff0c;又要交给运维去创建。那么程序员就需要把程序中运行的所有队列和交换机都写下来&#xff0c;…

用MySQL+node+vue做一个学生信息管理系统(一):配置项目

先用npm init -y生成配置文件 在项目下新建src文件夹&#xff0c;app.js文件。src目录用来放静态资源文件&#xff0c;app.js是服务器文件&#xff0c;index.js是vue的入口文件 使用npm install express下载express框架 在app.js文件夹开启node服务&#xff0c;监听的端口为…

Go语言--运算符

算术运算符 关系运算符 不能写0<a<10&#xff0c;要判断必须0<a&&a<10。因为int和bool不兼容 逻辑运算符 位运算符 赋值运算符 其他 运算符的优先级

国家海岸线变化评估:新英格兰和中大西洋沿岸海岸线的历史变化

National Assessment of Shoreline Change: Historical Shoreline Change along the New England and Mid-Atlantic Coasts 国家海岸线变化评估&#xff1a;新英格兰和中大西洋沿岸海岸线的历史变化 摘要 海滩侵蚀是美国许多公海沿岸的一个长期问题。随着沿岸人口的不断增加…

永辉超市购物卡有什么用?

感觉现在在超市买东西&#xff0c;还不如网购 这不&#xff0c;端午的时候&#xff0c;朋友送的永辉卡&#xff0c;一直没时间去用&#xff0c;我总担心过期 但是去了超市后&#xff0c;又不知道买什么&#xff0c;最后空手而归 还好收卡云可以回收永辉卡&#xff0c;两张三…

《C++20设计模式》适配器模式经验分享

文章目录 一、前言二、对于接口的讨论三、实现1、对象适配器1.1 UML类图1.2 实现 2、类适配器 四、最后 一、前言 从适配器模式开始就是类的组合聚合&#xff0c;类与类之间结构性的问题了。 适配器模式解决的问题&#xff1a; 适配器模式能够在不破坏现有系统结构的情况下&a…

mapreduce实现bean的序列化与反序列化

目录 序列化&#xff08;Serialization&#xff09; 反序列化&#xff08;Deserialization&#xff09; 事例操作 UserSale 重写序列化方法 重写反序列化 重写toString方法 SaleMapper SaleReducer SaleDriver 序列化&#xff08;Serialization&#xff09; 序列化是将…

【后端面试题】【中间件】【NoSQL】MongoDB的配置服务器、复制机制、写入语义和面试准备

MongoDB的配置服务器 引入了分片机制之后&#xff0c;MongoDB启用了配置服务器(config server) 来存储元数据&#xff0c;这些元数据包括分片信息、权限控制信息&#xff0c;用来控制分布式锁。其中分片信息还会被负责执行查询mongos使用。 MongoDB的配置服务器有一个很大的优…

WPF----自定义滚动条ScrollViewer

滚动条是项目当中经常用到的一个控件&#xff0c;大部分对外项目都有外观的需求&#xff0c;因此需要自定义&#xff0c;文中主要是针对一段动态的状态数据进行展示&#xff0c;并保证数据始终在最新一条&#xff0c;就是需要滚动条滚动到底部。 1&#xff0c;xaml中引入 <…

zxing-cpp+OpenCV根据字符串生成条形码

编译构建 需要使用到 CMake、Git、GCC 或 MSVC。 github 链接&#xff1a;https://github.com/zxing-cpp/zxing-cpp 编译之前请确保&#xff1a; 确保安装了 CMake 版本 3.15 或更高版本。 确保安装了与 C17 兼容的编译器(最低VS 2019 16.8 / gcc 7 / clang 5)。 编译构建…

Python面试宝典第4题:环形链表

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。如果存在环 &#xff0c;则返回 true 。 否则&#xff0c;返回 false 。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xf…

AI智能体|AI打工我躺平!使用扣子Coze智能体自动生成和发布文章到微信公众号(一)

大家好&#xff0c;我是无界生长&#xff0c;国内最大AI付费社群“AI破局俱乐部”初创合伙人。这是我的第 44 篇原创文章——《AI智能体&#xff5c;AI打工我躺平&#xff01;使用扣子Coze智能体自动生成和发布文章到微信公众号&#xff08;一&#xff09;》 AI智能体&#xf…

《涅朵奇卡:一个女人的一生》读后感

这周的计划是看完海明威的《丧钟为谁而鸣》&#xff0c;但是因为下班晚&#xff0c;而且书的体量大&#xff0c;所以只看了一半。本来以为这周的阅读计划完不成了&#xff0c;不料昨天加完班后拿起新到的《涅朵奇卡&#xff1a;一个女人的一生》&#xff0c;不自觉就陷进去了&a…

端口聚合基础知识

一、什么是端口聚合 端口聚合是将多个物理端口捆绑在一起&#xff0c;形成一个逻辑链路&#xff0c;以实现带宽增加、提高冗余和负载均衡的技术。端口聚合&#xff0c;也称为以太通道&#xff08;Ethernet Channel&#xff09;&#xff0c;主要用于交换机之间的连接。在具有多…