9-图论

1291 字
6 分钟
9-图论

图论#

图论基础#

无论无向图还是有向图 一般用vector<vector<int>> g; g.resize(n + 1, vector<int>()); 多源最短路径的时候用邻接矩阵,只有树的建图是特殊的 树就是无环连通图,无环图就是森林

  • 连通且无环
  • 连通且边数为 n−1, 防止孤立点导致剩下的有环
  • 无环且边数为 n−1,防止孤立点导致不连通
  • 任意两个顶点之间有且仅有一条简单路径

Huffman树#

最小的值放越下面,越大越接近根结点 取两个值最小的结点放最下面并形成根节点为其和

AOE网及关键路径#

//TODO

回溯#

循环可能的取值时对可能的取值进行判断和初始化相关控制变量,并在最开始的时候特判和初始化相关控制变量,如果进去说明判断成功,故终止条件是实指针

最短路#

单源最短路径#

朴素Dijkstra是初始化确认源点并松弛其出边, 然后每次确认尚未确认的点中dist最小的点并松弛其出边,堆优化Dijkstra是优先队列BFS,Bellman-ford是n次松弛通过出度松弛dist

Dijkstra#

设:

  • 当前从堆里取出的点是 u
  • 此时 dist[u] 是所有未确定点中最小的

假设 存在一条更短的路径s → ... → x → ... → u,长度 < dist[u]

其中:

  • x 是这条路径上 第一个尚未被确定的点
  • x 之前的点都已经被确定了

那么:

  1. x 的真实最短距离

    dist(x)真实路径到 u<dist[u]dist(x) \le \text{真实路径到 } u < dist[u]
  2. 而 Dijkstra 一定已经在处理 x 的前驱时:

    • 尝试过松弛 x
    • 所以 dist[x] 应该已经 ≤ 这个真实最短值
  3. 于是得到:

    dist[x]<dist[u]dist[x] < dist[u]
  4. 矛盾出现了

    • 既然 dist[x] < dist[u]
    • x 应该比 u 更早从堆里弹出
    • 不可能轮到 u

简而言之,假设a+b<c,b > 0,则有a < c - b < c,与 c < a矛盾,故不需比对已经确认的点,可以将最短遍历路上的其他点标为确认,dijkstra每次选最短的边是从已经遍历的点的所有出边里面选,可看作为小根堆,扩展了则出边出堆。

  1. 朴素
// 朴素Dijkstra
int dijkstra(int s, int n) {
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
for (int k = 0; k < n - 1; ++k) {
int t = 0;
for (int i = 1; i <= n; ++i) if (!st[i] && (!t || dist[i] < dist[t])) t = i;
for (int i = 1; i <= n; ++i) dist[i] = min(dist[i], dist[t] + g[t][i]);
st[t] = 1;
}
return dist[n] == INF ? -1 : dist[n];
}
  1. 堆优化
// 堆优化Dijkstra
int Dijkstra(int s, int n) {
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
priority_queue<PII, vector<PII>, greater<PII>> q;
q.push({0, s});
while(q.size()) {
PII t = q.top();
int ver = t.second, dis = t.first;
if (st[ver]) continue;st[ver] = 1;
for (int i = h[ver]; i != -1; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[ver] + w[i]) {
dist[j] = dist[ver] + w[i];
q.push({dist[j], j});
}
}
}
return dist[n] == INF ? -1 : dist[n];
}

Bellmanford#

// Bellmanford
int Bellmanford(int s, int n, int m) {
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
for (int k = 0; k < n; ++k) {
for (int i = 1; i <= n; ++i) {
int a = i;
for (int j = h[i]; j != -1; j = ne[j]) {
int b = e[j];
dist[b] = min(dist[b], dist[a] + w[i]);
}
}
}
return dist[n] > dist[n] >> 1 ? -1 : dist[n];
}

任意两点最短路径#

Floyd#

适合稠密图找任意两点的最短路径

void floyd(int n) {
for (int k = 1; k <= n; ++k) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
}

最小生成树#

Prim#

找未遍历过的最近点

Kruscal#

找最短的连接不同连通分量的边

至多 m - 2 次就能合并成功

BFS#

多源BFS#

多源BFS能遍历所有点的最小步骤数量取决于图里最长的一条路径,步数等于路径长度

DFS#

  1. 自顶向下的DFS为回溯
  2. 自底向上的DFS为DP 两种方向可以同时存在,因为这个方向说的是DFS函数帧里的参数,包括使用到的全局变量和函数参数

Tarjan#

求强连通分量#

const int N = 100005;
vector<int> g[N];
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int scc_id[N], scc_cnt;
void tarjan(int u) {
dfn[u] = low[u] = ++timestamp;
stk[++top] = u;
in_stk[u] = true;
for (int v : g[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (in_stk[v]) {
low[u] = min(low[u], dfn[v]);
}
}
// 找到一个 SCC
if (dfn[u] == low[u]) {
++scc_cnt;
int y;
do {
y = stk[top--];
in_stk[y] = false;
scc_id[y] = scc_cnt;
} while (y != u);
}
}

Tarjan 求割点(无向图)#

vector<int> g[N];
int dfn[N], low[N], timestamp;
bool is_cut[N];
void tarjan(int u, int parent) {
dfn[u] = low[u] = ++timestamp;
int child = 0;
for (int v : g[u]) {
if (!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
// 割点判断
if (parent != -1 && low[v] >= dfn[u])
is_cut[u] = true;
child++;
}
else if (v != parent) {
low[u] = min(low[u], dfn[v]);
}
}
// 根节点特殊判断
if (parent == -1 && child > 1)
is_cut[u] = true;
}

求桥#

struct Edge {
int to, id;
};
vector<Edge> g[N];
int dfn[N], low[N], timestamp;
bool is_bridge[N];
void tarjan(int u, int in_edge) {
dfn[u] = low[u] = ++timestamp;
for (auto [v, id] : g[u]) {
if (!dfn[v]) {
tarjan(v, id);
low[u] = min(low[u], low[v]);
// 桥判断
if (low[v] > dfn[u])
is_bridge[id] = true;
}
else if (id != in_edge) {
low[u] = min(low[u], dfn[v]);
}
}
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

9-图论
https://skaco2.com/posts/01-algorithm/9-图论/
作者
SKACO2
发布于
2026-04-09
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
SKACO2
Hello……
公告
欢迎来到我的博客!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
53
分类
8
标签
54
总字数
58,255
运行时长
0
最后活动
0 天前

目录