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;
}
其他重点图基础见:链接
浙公网安备 33010602011771号