2128 字
11 分钟
竞赛算法(5) 图论
2025-11-26
无标签

图论概念简述#

#

图(graph)是一个二元组G=(V,E)G = (V, E), 其中VV非空, 称为点集, EE称为边集.

VVEE均为有限集合时称为有限图, 否则称为无限图.

图可以被简单分类为无向图, 有向图和混合图.

每条边可以赋权.

图的顶点数量被称为图的阶.

相邻#

无向图中, 若存在连边e=(u,v)e = (u, v), 则称uuvvee是关联的(或相邻的), uuvv是相邻的. uu的邻居(邻域)指的是其所有相邻顶点组成的集合, 记作N(u)N(u). 对于点集SS来说, 其邻居(邻域)指的是其所有顶点的邻居取并集得到的集合, 记作N(S)N(S).

简单图#

自环: 对于边e=(u,v)e = (u, v), 若u=vu = v则称ee是一个自环.

重边: 若图中存在两条相同的边, 则称它们为一组重边.

无自环和重边的图被称为简单图. 否则被称为多重图.

具有至少两个顶点的简单无向图中一定存在度相同的点.

度数#

与顶点vv关联的边数称为顶点vv的度, 记作d(v)d(v), 特别地, 每个自环会对度数产生22的贡献, 而非11.

对于无向简单图, 有d(v)=N(v)d(v) = |N(v)|.

d(v)=0d(v) = 0, 则称vv为孤立点; 若d(v)=1d(v) = 1, 则称vv为叶节点/悬挂点. 若d(v)d(v)为偶数, 则称vv为偶点, 若d(v)d(v)为奇数, 则称vv为奇点.

vv与其他所有顶点相连, 则称vv为支配点. 在无向简单图中支配点的度数为V1|V| - 1.

握手定理(图论基本定理): 对于无向图, 所有顶点的度数之和等于两倍边的数目.

推论: 任意图中奇点有偶数个.

GG中所有节点度数的最小值称为图GG的最小度, 记作δ(G)\delta(G); 所有节点度数的最大值称为图GG的最大度, 记作Δ(G)\Delta(G).

在有向图中, 出边数量被称为出度, 记作d+(v)d^+(v), 入边数量被称为入度, 记作d(v)d^-(v). 显然有d+(v)+d(v)=d(v)d^+(v) + d^-(v) = d(v).

对于任何有向图, 有vVd+(v)=vVd(v)=E\displaystyle \sum_{v\in V}d^+(v) = \sum_{v\in V}d^-(v) = |E|.

若图GG中所有节点的度数均为kk, 则称GG为k-正则图.

如果给定序列aa, 可以找到某个图GG使得GGaa为度数列, 则称aa是可图化的; 如果能找到简单图以其为度数列, 则称aa是可简单图化的.

路径#

途径(walk): 连接一连串顶点的边的序列. 边的数量称作途径的长度.

迹(trail): 若途径中不存在相同的边, 则称为迹.

路径(path)或称简单路径(simple path): 若迹中连接的点序列两两不同, 则称为路径.

回路(circuit): 若迹起点与终点相同, 则称为回路.

环(cycle)或称简单回路(simple circuit): 若回路删去起点和终点后形成一条路径, 则称该回路为一个环.

子图#

若图G,HG, H满足V(H)V(G)V(H)\subset V(G), E(H)E(G)E(H) \subset E(G), 则称HHGG的子图, 记作HGH\subset G.

若对于HGH\subset G, 满足u,vV(H)\forall u, v\in V(H), 只要(u,v)E(G)(u, v)\in E(G), 就有(u,v)E(H)(u, v)\in E(H), 则称HHGG的导出子图/诱导子图(induced subgraph). 容易发现一个图的导出子图只由点集决定, 因此GG的点集为VV'的导出子图可以记作G[V]G[V'].

若对于HGH\subset G, V(H)=V(G)V(H) = V(G), 则称HHGG的生成子图(spanning subgraph).

若无向图GG的某个生成子图FF为k-正则图, 则称FFGG的一个k-因子.

对于有向图GG的某个导出子图G[V]G[V^*], 若VV^*中所有点在GG中的出边都被保留到了G[V]G[V^*]中, 则称G[V]G[V^*]GG的一个闭合子图.

连通#

对于无向图: 若存在点uu到点vv的途径, 则称uuvv连通. 由定义, 一个点与自己是连通的. 若无向图GG中任意两个顶点均连通, 则称GG是连通图, GG具有连通性. 极大连通子图称为连通分量.

对于有向图: 若存在uuvv的途径, 则称uu可达vv; 若节点两两互相可达, 则称该图为强连通图; 若将有向边替换为无向边后形成连通图, 则称该图为弱连通图. 类似地可以定义强连通分量和弱连通分量的概念.

建图#

邻接表#

使用结构体定义边, 使用的时候直接结构化绑定auto [y, dy] : adj[x]即可.

struct Edge {
int to;
int w;
};
// 带权图
vector<Edge> adj[N];
inline void buildgraph(int n) {
for (int i = 0; i <= n; i++) adj[i].clear();
}

遍历图#

DFS#

// vector<Edge> adj[N];
auto dfs = [&](int s) -> void {
stack<int> st;
vector<bool> vis(n + 1, 0);
st.push(s);
while (!st.empty()) {
int x = st.top();
st.pop();
if (vis[x]) continue;
vis[x] = 1;
for (const auto& [y, dy] : adj[x]) {
if (!vis[y]) st.push(y);
}
}
};

BFS#

// vector<Edge> adj[N];
auto bfs = [&](int s) -> void {
queue<int> q;
vector<bool> vis(n + 1, 0);
q.push(s);
while (!q.empty()) {
int x = q.front();
q.pop();
for (const auto& [y, dy] : adj[x]) {
if (!vis[y]) {
q.push(y);
vis[y] = 1;
}
}
}
};

拓扑排序#

得到有向无环图(DAG)的拓扑序列. 能够判断环. 进一步地, 拓扑排序可以用于解决有向无环图上的类DP问题. 如果某节点的状态只由其前驱节点决定, 那么就可以在拓扑排序中使用dp进行状态转移.

P1113 P4017 P1807

// vector<int> adj[N];
int ind[N];
vector<int> topseq;
auto toposort = [&]() {
queue<int> q;
for (int i = 1; i <= n; i++) {
if (ind[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
int x = q.front();
q.pop();
topseq.push_back(x);
for (const auto& y : adj[x]) {
// dp[y] = f(dp[x]);
ind[y]--;
if (ind[y] == 0) q.push(y);
}
}
return topseq.size() == n; // 拓扑排序是否成功, 不成功则说明有环.
};

最短路问题#

全源最短路#

Floyd算法#

基于简单的dp思想: x到y的最短路 = min(x经过k到y的最短路, x不经过k到y的最短路). 于是枚举每个k即可. 复杂度O(n3)O(n^3). 可以处理负权边, 不能处理负环.

ll f[N][N];
auto floyd = [&](int n) -> void {
// 初始化
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
f[i][j] = INFLL;
}
f[i][i] = 0;
for (const auto& [y, dy] : adj[i]) {
f[i][y] = dy;
}
}
// dp
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}
}
}
};

单源最短路#

正权图dijkstra#

// const ll INFLL = 1e18;
// using ll = long long;
// vector<Edge> adj[N];
// vector<ll> dis;
auto dijkstra = [&](int s = 1) -> void {
dis.assign(n + 1, INFLL);
using pli = pair<ll, int>;
priority_queue<pli, vector<pli>, greater<pli>> q;
q.emplace(0, s);
dis[s] = 0;
while (!q.empty()) {
auto [dx, x] = q.top();
q.pop();
if (dx > dis[x]) continue; // lazy deletion
for (const auto& [y, dy] : adj[x]) {
if (dis[x] + dy < dis[y]) {
dis[y] = dis[x] + dy;
q.emplace(dis[y], y);
}
}
}
};

欧拉图#

得名自哥尼斯堡七桥问题的解决者欧拉.

欧拉通路: 通过图中每条边恰好一次的迹(trail). 欧拉回路: 通过图中每条边恰好一次且起点等于终点的迹.

如果一个图中存在欧拉回路, 则称该图为欧拉图. 如果一个图中不存在欧拉回路但存在欧拉通路, 则称该图为半欧拉图.

欧拉图的性质

对于连通图GG, 以下几个性质是等价的:

  1. GG是欧拉图;
  2. GG的所有顶点度数都是偶数;
  3. GG可以被分解为若干条不共边回路的并.

trick合集#

  1. 对于网格图来说, 确定某点(x, y)的方法不一定需要直接知晓x和y这两个数. 假如我们可以知道(x + y, x - y)(即在主对角线上的坐标和副对角线上的坐标), 那么也可以确定唯一的一个点. 进一步说, 对于任意两个垂直的坐标轴y = -kx 和 y = 1 / k * x, 通过(x + ky, kx - y)可以确定唯一的一个点. 更进一步说, 两个坐标轴不需要垂直, 只需要保证两个坐标轴不平行, 都可以确定唯一一个点, 即(ax + by, cx + dy)其中ad != bc(行列式不为0), 即可确定唯一一个坐标.
  2. 曼哈顿距离的本质是一个斜放的正方形, 于是我们常可以根据曼哈顿距离确定主对角线上的坐标和副对角线上的坐标, 从而确定原点坐标. 2153B
竞赛算法(5) 图论
https://fuwari.vercel.app/posts/竞赛算法5-图论/
作者
ykindred
发布于
2025-11-26
许可协议
CC BY-NC-SA 4.0