「代码随想录算法训练营」第五十天 | 图论 part8

news/2024/10/4 5:29:25

目录
  • 拓扑排序
    • 题目:117. 软件构建
  • dijkstra(朴素版)
    • 题目:47. 参加科学大会
    • dijkstra算法和prim算法的区别
  • dijkstra(堆优化版)
    • 题目:47. 参加科学大会

拓扑排序

拓扑排序概括来说就是给出一个有向无环图,把这个有向无环图转成线性的排序,就叫拓扑排序。

使用广度优先搜索(BFS)即可。

image

如上图,当我们做拓扑排序的时候,优先找入度为0的节点,只有入度为0,它才是出发节点。

拓扑排序的过程:

  1. 找到入度为0的节点,加入结果集。
  2. 将该节点从图中移除。

循环以上两步,直到所有节点都在图中被移除了。

结果集的顺序,就是我们想要的拓扑排序顺序(结果集里顺序可能不唯一)

模拟过程:

image

image

image

image

image

image

判断有环:

image

这个图,我们只能将入度为0的节点0接入结果集。

之后,节点1、2、3、4形成了环,找不到入度为0的节点了,所以此时结果集里只有一个元素。

那么如果我们发现结果集元素个数不等于图中节点个数,我们就可以认定图中一定有有向环!

题目:117. 软件构建

题目链接:https://kamacoder.com/problempage.php?pid=1191
文章讲解:https://www.programmercarl.com/kamacoder/0117.软件构建.html
题目状态:看题解

思路:

使用拓扑排序。

代码:

#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>using namespace std;int main()
{int m, n, s, t;cin >> n >> m;vector<int> isDegree(n, 0); // 记录每个文件的入度unordered_map<int, vector<int>> umap; // 记录文件依赖关系vector<int> result; // 结果集while(m--){// s->t,先有s才能有tcin >> s >> t;isDegree[t]++; // t的入度加一umap[s].push_back(t); // 记录s指向哪些文件}queue<int> que;for(int i = 0; i < n; ++i){// 入度为0的文件,可以作为开头,先加入队列if(isDegree[i] == 0) que.push(i);}while(que.size()){int cur = que.front(); // 当前选中的文件que.pop();result.push_back(cur);vector<int> files = umap[cur]; // 获取该文件指向的文件if(files.size()) // cur有后续文件{for(int i = 0; i < files.size(); ++i){isDegree[files[i]]--; // cur的指向的文件入度-1if(isDegree[files[i]] == 0) que.push(files[i]);}}}if(result.size() == n){for(int i = 0; i < n - 1; ++i) cout << result[i] << " ";cout << result[n - 1];}elsecout << -1 << endl;return 0;
}

dijkstra(朴素版)

dijkstra算法:在有权图(权值非负数)中求从起点到其他节点的最短路径算法。

需要注意两点:

  • dijkstra算法可以同时求起点到所有节点的最短路径。
  • 权值不能为负数。

dijkstra算法和prim算法的计算过程非常类似:

  1. 第一步,选源点到哪个节点近且该节点未被访问过
  2. 第二步,该最近节点被标记访问过
  3. 第三步,更新非访问节点到源点的距离(即更新minDist数组)

其中minDist数组用来记录每一个节点距离源点的最小距离。

模拟过程:

image

image

image

image

image

image

image

image

image

题目:47. 参加科学大会

题目链接:https://kamacoder.com/problempage.php?pid=1047
文章讲解:https://www.programmercarl.com/kamacoder/0047.参会dijkstra朴素.html
题目状态:看题解

思路:

dijkstra算法。

代码:

#include <iostream>
#include <vector>
#include <climits>using namespace std;int main()
{int n, m, p1, p2, val;cin >> n >> m;vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));for(int i = 0; i < m; ++i){cin >> p1 >> p2 >> val;grid[p1][p2] = val;}int start = 1;int end = n;// 存储从源点到每个节点的最短距离vector<int> minDist(n + 1, INT_MAX);// 记录顶点是否被访问过vector<bool> visited(n + 1, false);minDist[start] = 0; // 起始点到自身的距离为0// 初始化路径数组vector<int> parent(n + 1, -1);// 遍历所有节点for(int i = 1; i <= n; ++i){int minVal = INT_MAX;int cur = 1;// 1.选距离源点最近且未访问过的节点for(int v = 1; v <= n; ++v){if(!visited[v] && minDist[v] < minVal){minVal = minDist[v];cur = v;}}// 2.标记该节点已被访问visited[cur] = true;// 3.更新非访问节点到源点的距离(即更新minDist数组)for(int v = 1; v <= n; ++v){if(!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]){minDist[v] = minDist[cur] + grid[cur][v];parent[v] = cur; // 记录边}}}// 不能到达终点if(minDist[end] == INT_MAX) cout << -1 << endl;// 到达终点最短路径else cout << minDist[end] << endl;// 输出最短情况for(int i = 1; i <= n; ++i) cout << parent[i] << "->" << i << endl;return 0;
}

dijkstra算法和prim算法的区别

  • prim是求非访问节点到最小生成树的最小距离
  • dijkstra是求非访问节点到源点的最小距离

dijkstra(堆优化版)

上一节的dijkstra算法是从节点的角度来遍历的,这一节的dijkstra算法我们从边的角度来遍历分析,且采用邻接表来存储图。邻接表表示一个有向有权图如下:

image

其中:

  • 节点1指向节点3,权值为1
  • 节点1指向节点5,权值为2
  • 节点2指向节点4,权值为7
  • 节点2指向节点3,权值为6
  • 节点2指向节点5,权值为3
  • 节点3指向节点4,权值为3
  • 节点5指向节点1,权值为10

dijkstra算法思路三部曲:

  1. 第一步,选源点到哪个节点近且该节点未被访问过
  2. 第二步,该最近节点被标记访问过
  3. 第三步,更新非访问节点到源点的距离(即更新minDist数组)

其中第一步我们要选择距离源点近的节点(即:该边的权值最小),所以我们需要一个小顶堆来帮我们对边的权值排序,每次从小顶堆堆顶去边就是权值最小的边。

因此在堆优化版本的dijkstra算法的三部曲中:

  1. 第一步:不用for循环去遍历,直接取堆顶元素
  2. 第二步:将节点做访问标记
  3. 第三步:和朴素dijkstra算法一样

题目:47. 参加科学大会

题目链接:https://kamacoder.com/problempage.php?pid=1047
文章讲解:https://www.programmercarl.com/kamacoder/0047.参会dijkstra朴素.html
题目状态:看题解

思路:

使用堆优化版本的dijkstra算法。

代码:

#include <iostream>
#include <vector>
#include <list>
#include <queue>
#include <climits>using namespace std;// 小顶堆
class mycomparison
{
public:bool operator()(const pair<int, int> &lhs, const pair<int, int> &rhs){return lhs.second > rhs.second;}
};// 定义一个结构体来表示带权重的边
struct Edge
{int to; // 邻接顶点int val; // 边的权重Edge(int t, int w): to(t), val(w) {} // 构造函数
};int main()
{int n, m, p1, p2, val;cin >> n >> m;vector<list<Edge>> grid(n + 1);for(int i = 0; i < m; ++i){cin >> p1 >> p2 >> val;// p1指向p2,权值为valgrid[p1].push_back(Edge(p2, val));}int start = 1; // 起点int end = n; // 终点// 存储从源点到每个节点的最短距离vector<int> minDist(n + 1, INT_MAX);// 记录顶点是否被访问过vector<bool> visited(n + 1, false);// 优先级队列中存放pair<节点, 源点到该节点的权值>priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pq;// 初始化队列,源点到源点的距离为0,所以初始为0pq.push(pair<int, int>(start, 0));// 起始点到自身的距离为0minDist[start] = 0;while(!pq.empty()){// 1.第一步,选源点到哪个节点近且该节点未被访问过(通过优先级队列来实现)// <节点, 源点到该节点的距离>pair<int, int> cur = pq.top();pq.pop();if(visited[cur.first]) continue;// 2.第二步,该最近节点被标记访问过visited[cur.first] = true;// 3.第三步,更新非访问节点到源点的距离(即更新minDist数组)for(Edge &edge : grid[cur.first]){// 遍历cur指向的节点,cur指向的节点为edge// cur指向的节点edge.to,这条边的权值为edge.valif(!visited[edge.to] && minDist[cur.first] + edge.val < minDist[edge.to]){// 更新minDistminDist[edge.to] = minDist[cur.first] + edge.val;pq.push(pair<int, int>(edge.to, minDist[edge.to]));}}}// 不能到达终点if(minDist[end] == INT_MAX) cout << -1 << endl;// 到达终点最短路径else cout << minDist[end] << endl;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

贵州省专业技术人员继续教育刷课脚本-JavaScript编写

脚本 学习网站:gzjxjy.gzsrs.cn、ghlearning.com、gzzj.ghlearning.com、... 脚本地址:贵州省专业技术人员继续教育-刷课脚本 教程 1.插件安装(以Microsoft Edge浏览器为例)打开最中间那个蓝色绿色的浏览器,谷歌之类的浏览器也可以点击屏幕右上角三个点,图示位置,然后点…

源码、反码和补码

对于有符号数而言,原码就是一个数的二进制表示。二进制的最高位是符号位,0 表示正数,1 表示负数。比如 56 是十进制,转为二进制就是 00111000,于是 56 的原码就是 00111000,左边第一位 0 是符号位,后面的其他数字是数据位。 计算机用数的原码进行显示,数的计算和存储是…

作业:自我介绍+软工5问

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-12这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-12/homework/13219这个作业的目标 学习编辑博客、预习教材介绍我自己 大家好,很高兴在这里与大家分享一些关于我的个人经历和兴趣爱…

洛谷团队

团队宣传!!! luogu团队!!! 网址:https://www.luogu.com.cn/team/79256 我的ID:sunhy2012 如要加入,信息请填“__sunhy2012__推荐”

陕西省专业技术人员继续教育刷课脚本-JavaScript编写

脚本 学习网站:陕西省专业技术人员继续教育学习平台: jxjy01.xidian.edu.cn 脚本地址:陕西省专业技术人员继续教育-刷课脚本 教程 1.插件安装(以Microsoft Edge浏览器为例)打开最中间那个蓝色绿色的浏览器,谷歌之类的浏览器也可以点击屏幕右上角三个点,图示位置,然后点击…

Go plan9 汇编: 打通应用到底层的任督二脉

原创文章,欢迎转载,转载请注明出处,谢谢。0. 前言 作为一个严肃的 Gopher,了解汇编是必须的。本汇编系列文章会围绕基本的 Go 程序介绍汇编的基础知识。 1. Go 程序到汇编 首先看一个简单到令人发指的示例: package mainfunc main() {a := 1print(a) }运行程序,输出: # …

20221414徐鹿鸣Markdown学习作业

问题一:哪些内容是你掌握的?哪些内容是你没有掌握的?使用AI推荐的工具或者你喜欢的工具实践一下没有掌握的内容。通义千问的回复 对我而言,Markdown的基础语法我接触过一二,但余下的高级语法、各种工具以及它与人工智能生成的内容的关联我并不清楚,下面是通过AI得知的一些…

Qt svg 图标图片不显示

因为少了Qt5Svg.dll库和imageformats文件夹的依赖,可以对对应Qt安装目录下的32位或者64位库文件夹中去找到。 自己开发了一个股票智能分析软件,功能很强大,需要的关注微信公众号:QStockView