1、判断割顶:
对于根节点当然简单,当且仅当它有两个或者是更多的子节点时,他才是割顶。
对于其他节点,
定理:在无向图连通图G的DFS树中,非根节点u是G的割顶当且仅当u存在一个子节点v,使得v及其所有后代都没有反向边连回u的祖先
#include <iostream> #include <cstdlib> #include <cstdio> #include <algorithm> #include <cstring> #include <stack> #include <vector> #include <queue> #include <map> using namespace std; const int maxn = 1000; vector<int> G[maxn]; int pre[maxn], dfs_clock, low[maxn], n; ///访问当前点的顺序标志,dfs_clock表示时间戳,low[v]为v及其后代所能练会的最早的祖先的pre值 bool iscut[maxn]; ///是否为割顶 void init() { for(int i=0; i<n; i++) G[i].clear(); memset(iscut, false, sizeof(iscut)); memset(pre, 0, sizeof(pre)); dfs_clock = 0; } int dfs(int u, int fa) //u在DFS树中的父结点是fa 返回u的最小low { int lowu = pre[u] = ++dfs_clock; int child = 0; //子结点数目 for(int i=0; i<G[u].size(); i++) { int v = G[u][i]; if(!pre[v]) //没有访问过v, 没有必要用vis标记了 { child++; int lowv = dfs(v, u); ///获得子节点最小low if(lowv >= pre[u]) iscut[u] = true; lowu = min(lowu, lowv); //用后代的 low 函数更新 u 的 low 函数 } else if(pre[v] < pre[u] && v != fa) //(u,v)为反向边 lowu = min( lowu, pre[v] ); //用反向边更新 u 的 low 函数 } if( fa < 0 && child < 2 ) //根节点当且仅当它有两个或者更多的子节点时才是割顶 iscut[u] = false; low[u] = lowu; return lowu; } int main() { int m, u, v; scanf("%d%d", &n, &m); init(); for(int i=0; i<m; i++) { cin>>u>>v; G[u].push_back(v); G[v].push_back(u); } for(int i=0; i<n; i++) if(!pre[i]) dfs(i, -1); for(int i=0; i<n; i++) //将割点输出 if(iscut[i]) printf("%d ", i); putchar('\n'); return 0; }
其他重点图基础见:链接