Template List:

最短路问题:Dijkstra(朴素版、堆优化版),Bellman-Ford,SPFA,Floyd

最小生成树:Prim、Kruskal

二分图问题:染色法、匈牙利算法

朴素Dijkstra:

适用:单源汇、无负边、稠密图 の 最短路问题

时间复杂度:o(n2)

Code:

 1 #define int longlong
2 const int INF = 0x3f3f3f3f3f3f3f3f;
3
4 int n, m, g[N][N], dist[N]; // 稠密图用邻接矩阵存图
5 bool st[N];
6
7 int dijkstra()
8 {
9 // 起点初始化为0, 其他点初始化为无穷大(INF)
10 memset(dist, 0x3f, sizeof dist);
11 dist[1] = 0;
12
13 for(int i = 1; i <= n; i++)
14 {
15 // 找到当前未确定最短路的点中 距离最短的点
16 int t = -1;
17 for(int j = 1; j <= n; j++)
18 if(!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
19 // 用t更新其他点
20 for(int j = 1; j <= n; j++)
21 dist[j] = min(dist[j], dist[t]+g[t][j]);
22 // t已确定最短路
23 st[t] = true;
24 }
25 // 若为INF说明无路可通
26 return dist[n] != INF ? dist[n] : -1;
27 }

堆优化Dijkstra:

适用:单源汇、无负边、稀疏图 の 最短路问题

时间复杂度:o(mlogn)

Code:

 1 #define int long long
2 #define PII pair<int,int>
3 const int INF = 0x3f3f3f3f3f3f3f3f;
4
5 int n, m, dist[N];
6 int h[N], e[N], w[N], ne[N], idx; // 邻接表存稀疏图
7 bool st[N];
8
9 int dijkstra()
10 {
11 memset(dist, 0x3f, sizeof dist);
12 dist[1] = 0;
13 // PII:first存dist,second存点的编号
14 // 小根堆维护当前未入st的dist最小值
15 priority_queue<PII, vector<PII>, greater<PII>> heap;
16 heap.push({0,1});
17
18 while(heap.size())
19 {
20 auto t = heap.top();
21 heap.pop();
22
23 int ver = t.second, d = t.first;
24 if(st[ver]) continue;
25 st[ver] = true;
26
27 for(int i = h[ver]; i != -1; i = ne[i])
28 {
29 int j = e[i];
30 if(dist[j] > dist[ver] + w[i])
31 {
32 dist[j] = dist[ver] + w[i];
33 heap.push({dist[j], j});
34 }
35 }
36 }
37
38 return dist[n] == INF ? -1 : dist[n];
39 }

Bellman-Ford:

适用:单源汇、有负边(可限制最多经过的边数) の 最短路问题,判是否存在负环

时间复杂度:o(nm)

Code:

 1 int n, m, k, dist[N], last[N];
2
3 struct Edge {
4 int x, y, w;
5 } edge[M]; // 存边
6
7 void bellman_ford()
8 {
9 // dist数组初始化
10 memset(dist, 0x3f, sizeof dist);
11 dist[1] = 0;
12 // 最多经过不超过i条边时的最短路
13 for(int i = 1; i <= k; i++)
14 {
15 // 复制原来的dist数组, 避免串联更新dist
16 memcpy(last, dist, sizeof dist);
17 for(int j = 1; j <= m; j++)
18 {
19 auto e = edge[j];
20 dist[e.y] = min(dist[e.y], last[e.x] + e.w);
21 }
22 }
23
24 if(dist[n] > INF / 2) puts("impossible"); // 大于一个较大的值就说明不存在通路
25 else printf("%lld\n", dist[n]);
26 }

SPFA:

适用:单源汇、有负边 の 最短路问题,判是否存在负环

时间复杂度:一般o(n),最差o(nm)

Code:

最短路问题:

 1 int n, m, dist[N];
2 int h[N], e[N], w[N], ne[N], idx; // 邻接表存图
3 bool st[N]; // 判断点是否在队列中
4
5 void spfa()
6 {
7 // 初始化dist, 将起点入队
8 memset(dist, 0x3f, sizeof dist);
9 dist[1] = 0;
10 queue<int> q;
11 q.push(1), st[1] = true;
12
13 while(q.size())
14 {
15 int t = q.front();
16 q.pop(), st[t] = false;
17 for(int i = h[t]; i != -1; i = ne[i])
18 {
19 int j = e[i];
20 if(dist[j] > dist[t] + w[i]) // dist[j]可更新变小 --> 用j更新其他点
21 {
22 dist[j] = dist[t] + w[i];
23 if(!st[j]) q.push(j), st[j] = true; // j不在队列中就入队
24 }
25 }
26 }
27
28 if(dist[n] == INF) puts("impossible"); // 无通路
29 else printf("%lld\n", dist[n]);
30 }

判有无负环:

 1 int n, m;
2 int h[N], e[M], w[M], ne[M], idx; // 邻接表存图
3 int dist[N], cnt[N]; // cnt数组记录到点i的最短路经过多少条边
4 bool st[N];
5
6 bool spfa()
7 {
8 queue<int> q;
9 for(int i = 1; i <= n; i++) q.push(i), st[i] = true;
10 while(q.size())
11 {
12 int t = q.front();
13 q.pop(), st[t] = false;
14 for(int i = h[t]; i != -1; i = ne[i])
15 {
16 int j = e[i];
17 if(dist[j] > dist[t] + w[i])
18 {
19 dist[j] = dist[t] + w[i];
20 cnt[j] = cnt[t] + 1;
21 if(cnt[j] >= n) return true; // 总共n个点, 经过>=n条边则必有负环
22 if(!st[j]) q.push(j), st[j] = true;
23 }
24 }
25 }
26 return false;
27 }

Floyd:

适用:多源汇 の 最短路问题

时间复杂度:o(n3)

Code:

 1 int n, m, k;
2 int d[N][N]; // 邻接矩阵存图, 跑完floyd后d[i][j]表示i到j的最短距离
3
4 void floyd()
5 {
6 for(int k = 1; k <= n; k++)
7 for(int i = 1; i <= n; i++)
8 for(int j = 1; j <= n; j++)
9 d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
10 }

朴素Prim:

适用:稠密图求最小生成树

时间复杂度:o(n2)

Code:

 1 int n, m, g[N][N], dist[N]; // 邻接矩阵存稠密图, dist数组表示点到集合中点的最短距离
2 bool st[N]; // 已入集合的点
3
4 int prim()
5 {
6 int res = 0;
7 memset(dist, 0x3f, sizeof dist);
8 for(int i = 0; i < n; i++)
9 {
10 int t = 0;
11 for(int j = 1; j <= n; j++)
12 if(!st[j] && (!t || dist[t] > dist[j])) t = j; // t是集合外中dist最小的点
13 if(i && dist[t] == INF) return INF; // 除第一个点外, 若dist为INF说明无最小生成树
14 if(i) res += dist[t]; // 除入集合的第一个点外, 累加边权
15 st[t] = true; // 将点入集合
16 for(int j = 1; j <= n; j++) dist[j] = min(dist[j], g[t][j]); // 用t更新未进入集合的点的dist
17 }
18 return res; // 返回累加的边权和
19 }

Kruskal:

适用:稀疏图求最小生成树

时间复杂度:o(mlogm)

Code:

 1 int n, m, fa[N]; // fa数组:并查集, 维护点集
2
3 struct Edge {
4 int a, b, w;
5 bool operator < (const Edge & W) const {
6 return w < W.w;
7 }
8 } e[M]; // 结构体数组存边
9
10 // 并查集中查找根节点的函数
11 int find(int x)
12 {
13 return fa[x] == x ? x : fa[x] = find(fa[x]);
14 }
15
16 int kruskal()
17 {
18 int res = 0, cnt = 0; // res:边权和, cnt:已连上的边数
19 sort(e+1, e+1+m); // 将边按边权从小到大排序
20 for(int i = 1; i <= n; i++) fa[i] = i; // 并查集初始化
21 for(int i = 1; i <= m; i++)
22 {
23 int a = find(e[i].a), b = find(e[i].b);
24 if(a != b) // a、b未连上
25 {
26 fa[a] = b; // 将点a、b连上
27 res += e[i].w, cnt ++;
28 }
29 if(cnt == n-1) return res; // 最小生成树已求出
30 }
31 return INF; // 最小生成树不存在
32 }

染色法:

适用:判断二分图

时间复杂度:o(n+m)

Code:

 1 int n, m;
2 int h[N], e[M], ne[M], idx; // 邻接表存图
3 int color[N]; // 0表示未染色, 1、2为染的不同颜色
4
5 bool dfs(int u, int c)
6 {
7 color[u] = c; // c表示染的颜色
8 for(int i = h[u]; i != -1; i = ne[i])
9 {
10 int j = e[i];
11 if(!color[j] && !dfs(j, 3-c)) return false; // 若未染色, 对j及j可达的点进行染色, 3-c:染不同颜色
12 else if(color[j] == c) return false; // 若j已染色, 但与u同色, 说明染色失败
13 }
14 return true; // 本次染色成功
15 }
16
17 bool stain()
18 {
19 for(int i = 1; i <= n; i++)
20 if(!color[i] && !dfs(i, 1)) return false; // 染色失败则不是二分图
21 return true; // 所有点染色成功则为二分图
22 }

匈牙利算法:

适用:二分图求最大匹配数

时间复杂度:理论:o(nm),实际:一般比 o(nm) 快

Code:

 1 int n1, n2, m; // n1为左点集, n2为右点集
2 int h[N], e[M], ne[M], idx; // 邻接表存图
3 int match[N]; // 与右点集的点匹配的左点集的点
4 bool st[N]; // 右点集的点是否已经考虑
5
6 bool find(int x) // 为左点集的点寻找匹配
7 {
8 for(int i = h[x]; i != -1; i = ne[i])
9 {
10 int j = e[i];
11 if(!st[j]) // 若j还未考虑过(避免重复考虑同一个点)
12 {
13 st[j] = true;
14 if(!match[j] || find(match[j])) // 若该点未匹配 或 该点匹配的点可找到下家
15 {
16 match[j] = x; // x与j匹配上
17 return true;
18 }
19 }
20 }
21 return false; // 未能为x找到匹配的点
22 }
23
24 int hungary() // 匈牙利算法
25 {
26 int cnt = 0; // 最大匹配数
27 for(int i = 1; i <= n1; i++) // 枚举左点集的点
28 {
29 memset(st, 0, sizeof st);
30 if(find(i)) cnt ++; // 匹配成功则cnt+=1
31 }
32 return cnt;
33 }

图论板子总结 / Graph Summary的更多相关文章

  1. 图论介绍(Graph Theory)

    1 图论概述 1.1 发展历史 第一阶段: 1736:欧拉发表首篇关于图论的文章,研究了哥尼斯堡七桥问题,被称为图论之父 1750:提出了拓扑学的第一个定理,多面体欧拉公式:V-E+F=2 第二阶段( ...

  2. 清北学堂模拟赛d1t4 一道图论好题(graph)

    题目描述 LYK有一张无向图G={V,E},这张无向图有n个点m条边组成.并且这是一张带权图,不仅有边权还有点权. LYK给出了一个子图的定义,一张图G’={V’,E’}被称作G的子图,当且仅当 ·G ...

  3. 论文解读(SUGRL)《Simple Unsupervised Graph Representation Learning》

    Paper Information Title:Simple Unsupervised Graph Representation LearningAuthors: Yujie Mo.Liang Pen ...

  4. 经典算法题每日演练——第十七题 Dijkstra算法

    原文:经典算法题每日演练--第十七题 Dijkstra算法 或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划” 这些经典 ...

  5. 经典算法题每日演练——第十四题 Prim算法

    原文:经典算法题每日演练--第十四题 Prim算法 图论在数据结构中是非常有趣而复杂的,作为web码农的我,在实际开发中一直没有找到它的使用场景,不像树那样的频繁使用,不过还是准备 仔细的把图论全部过 ...

  6. Individual Project - Word frequency program-11061171-MaoYu

    BUAA Advanced Software Engineering Project:  Individual Project - Word frequency program Ryan Mao (毛 ...

  7. 2017北京国庆刷题Day1 afternoon

    期望得分:100+100+100=300 实际得分:100+100+100=300 T1 一道图论好题(graph) Time Limit:1000ms   Memory Limit:128MB 题目 ...

  8. 从锅炉工到AI专家(7)

    说说计划 不知不觉写到了第七篇,理一下思路: 学会基本的概念,了解什么是什么不是,当前的位置在哪,要去哪.这是第一篇希望做到的.同时第一篇和第二篇的开始部分,非常谨慎的考虑了非IT专业的读者.希望借此 ...

  9. 深度学习之概述(Overview)

    2016年被称为人工智能的元年,2017年是人能智能应用的元年:深度学习技术和应用取得飞速发展:深度学习在互联网教育场景也得到广泛应用.本文主要介绍机器学习及深度学习之定义及基本概念.相关网络结构等. ...

随机推荐

  1. python appium server代码启动和关闭遇到的坑

    第一次使用博客,小白级,互相交流,有说的不对的地方欢迎来喷!!! 自动化的目的就是减少人工成本,如果每次跑自动化用例时都要手动起appium客户端,太费事了~~故,将appium server的调起和 ...

  2. .NET 中 GC 的模式与风格

    垃圾回收(GC)是托管语言必备的技术之一.GC 的性能是影响托管语言性能的关键.我们的 .NET 既能写桌面程序 (WINFROM , WPF) 又能写 web 程序 (ASP.NET CORE),甚 ...

  3. SQL注入靶场

    靶场搭建 系统环境&工具 环境采用centos7的版本(纯命令行),采用一键部署平台,phpstudy工具,安装教程链接:https://www.xp.cn/linux.html#instal ...

  4. 超全面!1.5w字总结50个Java经典基础面试题(已根据知识点分类)

    大家好,我是fancy. 在面试中将基础问题回答好就是成功的一半. 我总结了50道经典的Java基础面试题,里面包含面试要回答的知识重点,并且我根据知识类型进行了分类,可以说非常全面了. 小伙伴们点赞 ...

  5. 找到占用CPU最高的Java线程

    一.找到java进程id jps查看当前运行的java进程id [root@localhost ~]# jps 18354 Jps 9381 Bootstrap 二.找到内存和CPU占用最高的线程pi ...

  6. 12┃音视频直播系统之 WebRTC 实现1对1直播系统实战

    一.搭建 Web 服务器 前面我们已经实现过,但是没有详细说HTTPS服务 首先需要引入了 express 库,它的功能非常强大,用它来实现 Web 服务器非常方便 同时还需要引入 HTTPS 服务, ...

  7. Nexus5x 修改Android开机动画

    1.制作帧动画 这里随便从网上找了一个gif图片,导入PS中,打开后会形成很多帧图层,选择导航栏中的文件->脚本->将图层导出到文件可以将所有图层导出来.要注意文件命名,Android会按 ...

  8. MASA Auth - 权限设计

    权限术语 Subject:用户,用户组 Action:对Object的操作,如增删改查等 Object:权限作用的对象,也可以理解为资源 Effect:规则的作用,如允许,拒绝 Condition:生 ...

  9. 从零搭建Pytorch模型教程(四)编写训练过程--参数解析

    ​  前言 训练过程主要是指编写train.py文件,其中包括参数的解析.训练日志的配置.设置随机数种子.classdataset的初始化.网络的初始化.学习率的设置.损失函数的设置.优化方式的设置. ...

  10. SQL如何用表A更新表B

    文章标题很短,因为问题的描述过于具体,标题就会显得过长. 这个问题更为准确地描述应该是这样:表结构雷同或者有相似字段的两张表A和B,如何用A表的字段数据去更新B表字段的数据? 操作方法: 1 upda ...