tarjan 割点
P3388 【模板】割点(割顶)
割点判定
根结点:若根结点在 DFS 树中有两个或以上的子节点,则去除该根会增加连通分量,故为割点。
非根结点:若存在子节点 v 使得 low[v] >= dfn[u],则去掉 u 后,子树 v 及其后代无法通过返祖边回到 u 的祖先,u 为割点。
注意
访问时间戳 要++cnt
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 20000 + 5;
int n, m;
vector<int> G[MAXN]; // 邻接表存图
int dfn[MAXN], low[MAXN];
bool isCut[MAXN]; // 是否为割点
int cnt = 0;
/**
* Tarjan 割点算法主函数
* @param u 当前节点
* @param fa u 的父节点(用于避免走回父亲的边)
*/
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++cnt; // 设置访问时间戳 要++cnt,不能是cnt++, 否则第一个dfn会为0,导致后续if判断是否访问过出错
int child = 0; // u 的子节点数量
for (int v : G[u]) {
if (!dfn[v]) {
++child;
tarjan(v, u); // 递归处理子节点 v
low[u] = min(low[u], low[v]);
// 如果 u 不是根,并且 v 无法通过其他路径回到 u 或 u 的祖先
if (fa != -1 && low[v] >= dfn[u]) {
isCut[u] = true;
}
}
else if (v != fa) {
// 遇到返祖边,更新 low[u]
low[u] = min(low[u], dfn[v]);
}
}
// 根节点特判:若有两个及以上子节点,则是割点
if (fa == -1 && child > 1) {
isCut[u] = true;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
// 读入图
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u); // 无向图双向加边
}
// 多连通块处理:每个未访问点都进行一次 DFS
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
tarjan(i, -1); // -1 表示根节点无父亲
}
}
// 输出结果
vector<int> result;
for (int i = 1; i <= n; i++) {
if (isCut[i]) result.push_back(i);
}
cout << result.size() << "\n";
for (int i = 0; i < result.size(); i++) {
if (i > 0) cout << " ";
cout << result[i];
}
cout << "\n";
return 0;
}

浙公网安备 33010602011771号