割点和桥
一,一些基本概念
点双联通:在无向图中如果存在两个节点u,v连通,无论删去哪条边都不能使u,v不连通,
则称u,v边双联通。
边双联通:在无向图中如果存在两个节点u,v连通,无论删去那个点都不能使u,v不连通,
则称u,v点双联通。
割点:删去该点,图分裂为多个联通块。
割边(桥):删去该边,图分裂为多个联通块。
二,割点的求法
法一:可以假装割掉每个点,然后dfs看一下联通块的个数有没有增加,如果增加了,就证明这个点是一个割点。
法二:经典的tarjan算法,众所周知,tarjan算法是一个基于dfs树的算法。
这里主要总结了效率较高的tarjan算法(注意tarjan有很多种,此tarjan非彼tarjan)。
因为一棵连通图的dfs树可以包含图上的所有节点,而割点的定义为去除这个点,原图分裂为多个连通块,如果某个
点是根节点,以它为dfs树的根节点,它有大于等于2个子节点,那么显而易见,割掉它原图会分裂成许多个联通块。
如果某个点不是根节点那么怎么判断它是不是割点呢,如果这个点的后继节点无法通过不是搜索树上的边找到这个点
的父节点那么割掉这个点原图会分裂成多个联通块。
为此我们依然引入dfn[k]与low[k]这两个时间戳。
low[k]代表节点k能通过树枝边和前向边能够追溯到的搜索树上最老的节点。如果某一个点与其后继节点的关系为
dfn[k]<=low[e],则代表点k为割点。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #define maxn 20005 6 7 using namespace std; 8 9 struct node 10 { 11 int ed,nxt; 12 }; 13 node edge[100005<<1]; 14 int n,m,first[maxn],cnt; 15 int dfn[maxn],low[maxn],tim; 16 bool cut[maxn]; 17 int tot; 18 19 inline void add_edge(int s,int e) 20 { 21 cnt++; 22 edge[cnt].ed=e; 23 edge[cnt].nxt=first[s]; 24 first[s]=cnt; 25 return; 26 } 27 28 inline void tarjan(int k,int root) 29 { 30 dfn[k]=low[k]=++tim; 31 int child=0; 32 for(register int i=first[k];i;i=edge[i].nxt) 33 { 34 int e=edge[i].ed; 35 if(dfn[e]==0) 36 { 37 child++; 38 tarjan(e,root); 39 low[k]=min(low[k],low[e]); 40 if((k!=root&&dfn[k]<=low[e])||(k==root&&child>=2)) 41 cut[k]=true; 42 } 43 else low[k]=min(low[k],dfn[e]); 44 } 45 return; 46 } 47 48 int main() 49 { 50 scanf("%d%d",&n,&m); 51 for(register int i=1;i<=m;++i) 52 { 53 int s,e; 54 scanf("%d%d",&s,&e); 55 add_edge(s,e); 56 add_edge(e,s); 57 } 58 for(register int i=1;i<=n;++i) 59 if(dfn[i]==0) tarjan(i,i); 60 for(register int i=1;i<=n;++i) 61 if(cut[i]) tot++; 62 printf("%d\n",tot); 63 for(register int i=1;i<=n;++i) 64 if(cut[i]) printf("%d ",i); 65 return 0; 66 }