无向图连通度(割) – (参考的一个acm模板,不知道挂啥链接)
算法思想
观察 dfs 搜索树,我们可以发现有两类节点可以成为割点:
-
对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;
-
对非叶子节点u(非根节点),若其子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再连通;则节点u为割点。
-
设置vis, pre, anc数组,cnt记录dfs树中的孩子个数。
- pre[cur]:cur结点dfs时访问时的时间’。
- anc[cur]: 记录cur结点及其子孙结点最早祖先的dfs时间(朔源到尽头)。
- vis标记结点访问状态:0:未访问, 1:正在访问, 2:访问结束。
-
当(u,v)为树边且 anc[v] >= dfn[u] 时,节点u才为割点。该式子的含义:以节点v为根的子树所能追溯到最早的祖先节点要么为v要么为u,注意 u 必须不是根结点。
邻接矩阵版
/*==================================================*\
| 无向图连通度(割)
| INIT: edge[][]邻接矩阵;vis[],pre[],anc[],deg[]置为0;
| CALL: dfs(0, -1, 1, n);
| k=deg[0], deg[i]+1(i=1…n-1)为删除该节点后得到的连通图个数
| 注意:0作为根比较特殊!
\*==================================================*/
int edge[V][V], anc[V], pre[V], vis[V];
vector<vector<int> > ans;
void dfs(int cur, int father, int dep, int n){ // vertex: 0 ~ n-1
vis[cur] = 1; //开始访问
pre[cur] = anc[cur] = dep;
int cnt = 0;
for(int i = 0; i < n; ++i)
if(edge[cur][i]){
if(i != father && 1 == vis[i]){ //i访问还未结束,所以为cur的祖先
if(pre[i] < anc[cur]) //更新cur的最早祖先
anc[cur] = pre[i]; //back edge
}
if(0 == vis[i]){ //tree edge,i还未访问,搜索树的树边
dfs(i, cur, dep+1, n);
cnt++;
if(anc[i] < anc[cur]) //如果cur的最小祖先大于i的最小祖先,且存在cur->i,则更新
anc[cur] = anc[i]; //cur的最小祖先
if((cur == 0 && cnt > 1) && (cur != 0 && anc[i] >= pre[cur])) //如果cur的搜索时间比child的最早的祖先时间还小或者相等,则找到一个割点(child及其子孙最早也只能溯源到cur), 注意cur!=根节点
deg[cur]++;
}
}
vis[cur] = 2; //结束访问
}
邻接表版
int anc[V], pre[V], vis[V], deg[V];
vector<int> adj[V];
void dfs(int cur, int father, int dep, int n){ // vertex: 0 ~ n-1
vis[cur] = 1; //开始访问
pre[cur] = anc[cur] = dep;
int cnt = 0;
for(int i = 0; i < adj[cur].size(); ++i){
int child = adj[cur][i];
if(child != father && 1 == vis[child]){ //child访问还未结束,所以为cur的祖先
if(pre[child] < anc[cur]) //更新cur的最早祖先
anc[cur] = pre[child]; //back edge
}
if(0 == vis[child]){ //tree edge, child还未访问,搜索树的树边
dfs(child, cur, dep+1, n);
cnt++;
if(anc[child] < anc[cur]) //如果cur的最小祖先大于child的最小祖先,且存在cur->child,
anc[cur] = anc[child]; //则更新cur的最小祖先
if((cur == 0 && cnt > 1) || (cur != 0 && anc[child] >= pre[cur])) //如果cur的搜索时间比child的最早的祖先时间还小或者相等,则找到一个割点(child及其子孙最早也只能溯源到cur), 注意cur!=根节点
deg[cur]++;
}
}
vis[cur] = 2; //结束访问
}