【图论】割点(割顶)
前置定义
有无向图
- 无向图的 DFS 树:从某一点
开始 DFS,访问邻点 当搜索到点 时,我们遍历每一条以 为起点的边 ,且定义有向边 于是 DFS 的过程全部完成之后,所有被定义的有向边就会组成一颗以 为根树,这棵树就是图 的 DFS 树。 - DFS 序:DFS 过程中,每次搜索到的节点编号组成的序列是为 DFS 序。
- DFN(DFS number):节点在 DFS 序中的位置,即这个节点是第几个被 DFS 查找到的。下称 节点
的 DFN 为 。
上图展示了某图的一种 DFS 树以及这种 DFS 序下对应的节点的 DFN 值。
DFS 树的重要性质
定义
- DFN 单调性:任意一个节点
,满足它的子树中所有的点的 值均大于等于它的 值。即 . - 祖先后代性:若
为非树边,则必有 有祖先和后代的关系。
割点判断
所谓割点,是指在删除这个节点之后,整个图的连通块数量有增加,也就是此次删除把某个连通块分割成了两个连通块。所以整个图原来有几个连通块并不重要,重要的是删点后能否分割当前的连通块。只需思考一个连通块内求割点的方法,就可以推广到有几个连通子图的复杂图中。
如果
设
如果这个节点为根节点,那么就要判断它的子树数量。如果它有超过一个的子树(即儿子),那么如果删去它,他的子树之间都不会联通了。这个判断可以通过 DFS 时通过树边访问时记录。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m, num, root, cv_cnt;
int dfn[N], low[N];
bool cv_chk[N];
vector<int> g[N];
inline int read(){
int s = 0, w = 1;
char ch = getchar();
while(!isdigit(ch)) { w = (ch == '-' ? -1 : 1); ch = getchar(); }
while(isdigit(ch)) { s = s * 10 + ch - '0'; ch = getchar(); }
return s * w;
}
inline void write(int x){
if(x < 0) { x = -x, putchar('-'); }
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
void cv(int x){
int chc = 0;
dfn[x] = low[x] = ++num;
for(int y : g[x]){
if(!dfn[y]){
cv(y);
chc++, low[x] = min(low[x], low[y]);
if(low[y] >= dfn[x]){
if(x != root || chc > 1){
if(!cv_chk[x])
cv_cnt++;
cv_chk[x] = true;
}
}
}else
low[x] = min(low[x], dfn[y]);
}
}
int main(){
n = read(), m = read();
while(m--){
int u = read(), v = read();
g[u].push_back(v), g[v].push_back(u);
}
for(int i = 1; i <= n; i++)
if(!dfn[i])
root = i, cv(i);
write(cv_cnt); putchar('\n');
for(int i = 1; i <= n; i++)
if(cv_chk[i])
write(i), putchar(' ');
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」