图论算法总结
求最短路径
迪杰斯特拉
- 不能用于有负权环的图
- 迪杰斯特拉用了贪心原理,负权图
dijkstra 普通版
每次找到一个离源点最近的点,标记,然后更新经过该点的路
优化版 dijkstra
用优先队列存待标记点,更新路径时,有松弛操作时把这个被更新的点入队
bellman_ford
- 适用于对路径长度有要求的题,例如,求1到n之间,路径长度小于k的最短路径。
思路:循环k次每次松弛可以松弛的点,被松弛的数组要备份,防止重复松弛。
spfa
spfa 求最短路径
和迪杰斯特拉很像,每次找到离原点最近的点,然后,出队,取消标记,当有松弛操作且这个点不在队列中时,入这个点,标记这个点。
spfa 判断负环
用cnt数组记录更新次数,每次松弛次数等于上一个节点更新次数加一,当更新次数大于等于m时说明有负环。
Floyd
- 求多源最短路
for (int i = 0 ~ n - 1)
for (int j = 0 ~ n - 1)
for (int k = 0 ~ n - 1)
d[j] =min(d[j] , d[k] + m[k][j]);
最小生成树
prim
先将任意点,把这个点到集合的距离置0, 然后,找离集合最近的点加入集合。然后更新每个和这个点有连接点到集合的距离。每次加入点连接集合的路就是最小生成树的边。
Kruskal 克鲁斯特
把边按权值从小到大排列,遍历边,当这边连接的是两个集合时,,这个边就是最小生成树的边。
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int n, m, x, y, w;
const int N = 1e5 + 10, M = 2e5 + 10;
int p[N];
struct Edge {
int x, y, w;
inline bool operator>(const Edge& other) const {
return other.w < w;
}
}eds[M];
int find(int x) {
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
int main() {
int ans = 0;
priority_queue<Edge, vector<Edge>, greater<Edge>> q;
cin >> n >> m;
for (int i =1; i <= n; i++) p[i] = i;
while(m--) {
cin >> x>>y>>w;
q.push({x, y, w});
}
while(q.size()) {
auto t = q.top();
q.pop();
int tx = t.x, ty = t.y;
int px = find(tx), py = find(ty);
if (px != py) {
p[px] = py;
ans += t.w;
}
}
int t = find(1);
for (int i = 2; i<= n; i++) if (find(i) != t) {
cout << "impossible" << endl;
return 0;
}
cout<< ans;
return 0;
}
次小生成树
代码
最小生成树原理及证明:
-
1、权值最小的边可能为最小生成树的边
-
2、不在最小生成树的边中最小边可能为最小生成树的边
二分图原理证明
二分图性质
- 最小点覆盖 = 最大匹配数
强连通分量
tarjan求scc(强连通分量)
tarjan算法模板
为什么这里不能用low[j] 替换
这个是有向边,也就是说在栈中但是不代表u可以到大j能到达的所有点。
tarjan求割点
割点的定义
tarjan算法 求割点求法
如果是根节点
非根节电
tarjan求割点算法模板
tarjan算法
求解有向图强连通分量的线性时间的算法。