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

【数据结构】B : DS图应用--最短路径

B : DS图应用–最短路径

文章目录

  • B : DS图应用--最短路径
    • Description
    • Input
    • Output
    • Sample
        • Input
      • Output
    • 解题思路:
      • 初始化
      • 主循环
      • 心得:
    • AC代码

Description

给出一个图的邻接矩阵,再给出指定顶点v0,求顶点v0到其他顶点的最短路径

Input

第一行输入t,表示有t个测试实例
第二行输入n,表示第1个图有n个结点
第三行起,每行输入邻接矩阵的一行,以此类推输入n行
第i个结点与其他结点如果相连则为1,无连接则为0,数据之间用空格隔开
第四行输入v0,表示求v0到其他顶点的最短路径距离
以此类推输入下一个示例

Output

每行输出v0到某个顶点的最短距离和最短路径
每行格式:v0编号-其他顶点编号—-[最短路径],具体请参考示范数据

Sample

Input
1
5
0 5 0 7 15
0 0 5 0 0
0 0 0 0 1
0 0 2 0 0
0 0 0 0 0
0

Output

0-1-5----[0 1 ]
0-2-9----[0 3 2 ]
0-3-7----[0 3 ]
0-4-10----[0 3 2 4 ]

解题思路:

首先先要了解图论里面单源最短路径的实现——Dijkstra算法,知道它是怎么一步步算出源点到每一个点的最短距离的,可以参考这个视频【算法】最短路径查找—Dijkstra算法,然后就看代码,对着视频来进行解释:

// Dijkstra算法实现
void Dijkstra(int start)
{memset(dis, 0x3f, sizeof(dis));memset(fixed, 0, sizeof(fixed));last[start] = -1;dis[start] = 0;int minDisNode, minDis;for (int i = 0; i < n; i++){minDis = INF;for (int j = 0; j < n; j++)if (!fixed[j] && dis[j] < minDis)minDisNode = j, minDis = dis[j];fixed[minDisNode] = true;for (int j = 0; j < n; j++){if (g[minDisNode][j] != 0 && minDis + g[minDisNode][j] < dis[j])dis[j] = minDis + g[minDisNode][j], last[j] = minDisNode;}}return 0;
}

初始化

memset(dis, 0x3f, sizeof(dis));  // 设置所有节点到源点的初始距离为无穷大
memset(fixed, 0, sizeof(fixed)); // 初始化所有节点为未固定
last[start] = -1;                // 源点的上一个节点设置为-1
dis[start] = 0;                  // 源点到自身的距离设置为0
  • dis[]数组存储从源点到每个节点的当前最短距离。初始时,除了源点到自身的距离为0外,所有其他距离都设置为无穷大。
  • fixed[]数组用于标记节点是否已经找到了从源点出发的最短路径。
  • last[]数组用于记录到达每个节点的最短路径上的前一个节点,对于源点而言,没有前一个节点,所以设置为-1。

主循环

for (int i = 0; i < n; i++)
{minDis = INF;for (int j = 0; j < n; j++)if (!fixed[j] && dis[j] < minDis)minDisNode = j, minDis = dis[j];fixed[minDisNode] = true;for (int j = 0; j < n; j++){if (g[minDisNode][j] != 0 && minDis + g[minDisNode][j] < dis[j])dis[j] = minDis + g[minDisNode][j], last[j] = minDisNode;}
}
  • 第一个for循环遍历所有节点,寻找最短路径。
  • 内层的第一个for循环用于找到当前未固定节点中距离源点最近的节点minDisNode
  • fixed[minDisNode] = true;将找到的最短距离节点标记为已固定。
  • 内层的第二个for循环进行“松弛操作”:通过minDisNode更新其他未固定节点的最短距离。如果通过minDisNode到某个节点的距离比当前记录的距离短,则更新该距离
  • 松弛操作:if (g[minDisNode][j] != 0 && minDis + g[minDisNode][j] < dis[j])检查是否存在一条从minDisNodej的边,并且通过这条边到达j的距离是否比当前记录的距离短。如果是,更新dis[j]为通过minDisNodej的新距离,并记录last[j]minDisNode

心得:

一开始学这个算法的时候,可能会想到一个环,对于这个环,例如一个三个节点的环,现在节点一是源点,懵的地方就在于我在第一个次循环之后得出节点一到节点二最短,我就把节点二纳入fixed中,我就有疑惑,如果是一个环的话,那我从节点一到节点三再到节点二为什么不是最短。现在项想明白,在循环内层第一个for循环的时候,就已经挑选出最短的了,哪怕节点一到节点二和节点一到节点三的距离相等,节点三到节点二总不可能为负数吧。

明白这个算法的原理之后,后面的输出就很简单了,直接上代码吧。

AC代码

#include <iostream>
#include <vector>
using namespace std;const int INF = 999999; // 定义无穷大常量void printShortestPath(int u, const vector<int>& previousNodes) {if (u == -1)return;printShortestPath(previousNodes[u], previousNodes);cout << u << " ";return;
}void calculateShortestPaths(int start, const vector<vector<int>>& graph, int n) {vector<int> previousNodes(n, -1);vector<int> shortestDistances(n, INF);vector<int> visitedNodes(n, 0);shortestDistances[start] = 0;for (int i = 0; i < n; i++) {int minDistance = INF, nearestNode = -1;for (int j = 0; j < n; j++)if (!visitedNodes[j] && shortestDistances[j] < minDistance){nearestNode = j;minDistance = shortestDistances[j];}visitedNodes[nearestNode] = 1;for (int j = 0; j < n; j++)if (graph[nearestNode][j] != 0 && minDistance + graph[nearestNode][j] < shortestDistances[j]){shortestDistances[j] = minDistance + graph[nearestNode][j];previousNodes[j] = nearestNode;}}for (int i = 0; i < n; i++) {if (i != start) {cout << start << "-" << i << "-" << shortestDistances[i];cout << "----[";printShortestPath(i, previousNodes);cout << "]" << endl;}}return;
}int main() {int t;cin >> t;while (t--) {int n;cin >> n;vector<vector<int>> graph(n, vector<int>(n));for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)cin >> graph[i][j];int sourceNode;cin >> sourceNode;calculateShortestPaths(sourceNode, graph, n);}return 0;
}

相关文章:

  • Windows任务管理器内存性能界面各个参数含义
  • 【操作系统】线程的状态
  • OpenCV将两张图片拼接成一张图片
  • 有了倾斜摄影,如何搭建一座智慧城市?
  • CMakeLists.txt:打印find_package变量;判断库文件路径设定是否正确;install文件设置
  • Ps:裁剪工具 - 裁剪预设的应用
  • 深入理解C语言指针基础概念:定义、内存地址与声明初始化
  • 2023年【制冷与空调设备安装修理】考试报名及制冷与空调设备安装修理考试资料
  • 2023APMCM亚太杯数学建模选题建议及初步思路
  • Android: ListView + ArrayAdapter 简单应用
  • 任意文件下载漏洞(CVE-2021-44983)
  • Java WebSocket框架
  • WPS或Excel查找A列中有B列没有的值
  • redis运维(十八)pipeline
  • 二维数值型数组例题
  • 【5+】跨webview多页面 触发事件(二)
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Laravel Mix运行时关于es2015报错解决方案
  • Laravel 实践之路: 数据库迁移与数据填充
  • ReactNative开发常用的三方模块
  • Terraform入门 - 3. 变更基础设施
  • Web标准制定过程
  • 工程优化暨babel升级小记
  • 来,膜拜下android roadmap,强大的执行力
  • 如何在 Tornado 中实现 Middleware
  • 三栏布局总结
  • 译有关态射的一切
  • 原生Ajax
  • 责任链模式的两种实现
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ​2020 年大前端技术趋势解读
  • ​虚拟化系列介绍(十)
  • #### golang中【堆】的使用及底层 ####
  • #100天计划# 2013年9月29日
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (2)Java 简介
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (二开)Flink 修改源码拓展 SQL 语法
  • (二刷)代码随想录第16天|104.二叉树的最大深度 559.n叉树的最大深度● 111.二叉树的最小深度● 222.完全二叉树的节点个数
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (四十一)大数据实战——spark的yarn模式生产环境部署
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (译) 函数式 JS #1:简介
  • (原創) 物件導向與老子思想 (OO)
  • (转) RFS+AutoItLibrary测试web对话框
  • (转)人的集合论——移山之道
  • (状压dp)uva 10817 Headmaster's Headache
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • . NET自动找可写目录
  • .Net Core 笔试1
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .net framework 4.0中如何 输出 form 的name属性。