什么叫做割点?
在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合。
图中c就是一个割点,剩下{A,B}和{C,D}两个连通分量
实现原理:
在学习割点的基础上应该了解什么是连通分量和Tanjan的实现过程。
求割点的过程主要是在Tanjan模板中加了一些判断。
Tanjan的过程主要就是DFS,在DFS树中,我们发现有两种点可以成为割点:
①对于根节点,如果它有两个或两个以上的儿子,则该根节点为割点(注意儿子数量均为DFS树中的儿子个数)
②对于非叶子结点s,如果它的儿子结点e不通过s就可以访问s的祖先结点的话,那么s点就不是割点。反之,只要存在一个儿子结点e必须通过s来访问s的祖先结点的话,那么s就是一个割点。
判断条件:
对于跟结点我们上面提过了,只需要判断DFS树中根节点的儿子数量。
对于非叶子结点:
我们用 dfn[u] 记录节点u在DFS过程中被遍历到的次序号, low[u] 记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小),那么 low[u] 的计算过程如下:
模板题:POJ1523
题意:给你一个无向图,要求你输出割点的标号和图连通分量的个数
思路:判断割点就用模板,问题是图联通分量的个数是多少?
①如果根节点是割点,那么图的连通分量个数就是DFS树中根节点的儿子数量
②如果非叶子结点是割点,那么图的连通分量的个数就是DFS树中满足条件 low[v]>=dfn[u] (v是u的儿子)的个数 +1
附上代码(含解析)
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
struct node
{
int e;
int p;
}load[1000005];
int head[1005],sign;
void add_edge(int s,int e)
{
load[++sign]=node{e,head[s]};
head[s]=sign;
}
int dfn[1005],low[1005],time;
int cut[1005];
///记录删除该点可以分成几个连通分量
void tanjan(int s,int pre)
{
dfn[s]=low[s]=++time;
int son=0,cnt=1;
/*
cnt用于非根节点
cnt初始化为1,因为如果s为割点。
去掉s留下的连通分量个数=儿子中满足(low[e]>=dfn[s])的个数+1
*/
///son用于记录儿子个数,用于根结点的
for(int i=head[s];i!=-1;i=load[i].p)
{
int e=load[i].e;
if(e!=pre)
{
if(!dfn[e])///如果没有访问过
{
son++; ///儿子的个数+1
tanjan(e,s);
low[s]=min(low[s],low[e]);
///如果当前不为根节点
///并且e必须通过s访问s的祖先
if(pre!=-1&&low[e]>=dfn[s])
cut[s]=++cnt;
}
else
low[s]=min(low[s],dfn[e]);
}
}
/*
如果当前s为根节点,且儿子个数>=2,
那么s为割点,去掉s后的连通分量数量
就等于儿子的个数
*/
if(pre==-1&&son>=2)
cut[s]=son;
}
void init()
{
time=sign=0;
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(cut,0,sizeof(cut));
///去掉第i个点,剩余连通分量的个数
///cnt[i]只能=0或者>=2
///=0即该点不是割点
}
int main()
{
int n,s,e,cas=0;
while(scanf("%d",&s)!=EOF,s)
{
init(); ///初始化
scanf("%d",&e);
add_edge(s,e);
add_edge(e,s);
n=max(s,e);
while(scanf("%d",&s)!=EOF,s)
{
scanf("%d",&e);
add_edge(s,e);
add_edge(e,s);
n=max(e,s);
}
tanjan(1,-1);
if(++cas!=1)
printf("\n");
printf("Network #%d\n",cas);
int flag=1;
for(int i=1;i<=n;i++)
{
if(cut[i])
{
printf(" SPF node %d leaves %d subnets\n",i,cut[i]);
flag=0;
}
}
if(flag)
printf(" No SPF nodes\n");
}
return 0;
}
/*
1 2
1 3
1 4
1 5
2 3
0
答案
Network #1
SPF node 1 leaves 3 subnets
*/
参考博客:https://www.cnblogs.com/nullzx/p/7968110.html
http://www.cnblogs.com/en-heng/p/4002658.html