by GeneralLiu
tarjan 求 割点 割边
无向图 的 割点 割边:
对于无向连通图来说,
如果删除 一个点以及与它相连的边 之后,
使得这个图不连通,
那么该点为割点 ;
如果删除 一条边 之后 ,
使得这个图不连通,
那么该边为割边 ;
tarjan 是基于 dfs树 的算法
所以, dfs树 上的一些 术语有必要知道 一下
so 看我 博客
与 有向图的tarjan算法 非常类似
割边 的 求法 (这个一步就判断出来,先写容易的):
在 dfs树 上 后向边 一定不是 割边
如果是 树边(from u,to v) // 对应 下文 代码 20 行
且 low [ v ] > dfn [ u ] // 对应 下文 代码 24,25 行
则 是割边
在 无向图 这里 边的类型只有这两种(没有横叉边与前向边)
割点 的 求法 :
如果是 dfs树 的 根节点
且 有不止一个儿子 则 是割点 // 对应 下文 代码 33,34 行
不是根
如果 u 存在子节点 v // 对应 下文 代码 28,29 行
使 low[v] >= dfn[u]
那么u为割点
代码
与 有向图的tarjan代码 非常类似
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 #define N 1000 5 #define M 2000 6 int dfn[N],low[N],cnt,n,m,head[N],to[M],next[M]; 7 bool cutnode[N],cutedge[M]; 8 void add(int x,int y){ 9 next[++cnt]=head[x]; 10 to[cnt]=y; 11 head[x]=cnt; 12 } 13 void dfs(int fa,int u){ 14 dfn[u]=low[u]=++cnt; 15 int v,ch=0; 16 bool b=0; 17 for(int i=head[u];i;i=next[i]){ 18 v=to[i]; 19 if(v==fa)continue; 20 if(!dfn[v]){ // 树边 21 ch++; 22 dfs(u,v); 23 low[u]=min(low[u],low[v]); 24 if(low[v]>dfn[u]) // 判断 割边 25 cutedge[(i+1)>>1]=1; // 无向图边存了两遍 如此来定位 边的编号 26 } 27 else low[u]=min(low[u],dfn[v]); 28 if(low[v]>=dfn[u]) // 判断 割点 29 b=1; 30 } 31 if(dfn[u]!=1) // 讨论 u 是否 为根 分别处理 32 cutnode[u]=b; 33 else if(ch>=2) 34 cutnode[u]=1; 35 } 36 int main(){ 37 scanf("%d%d",&n,&m); 38 for(int x,y,i=1;i<=m;i++){ 39 scanf("%d%d",&x,&y); 40 add(x,y); 41 add(y,x); 42 } 43 for(int i=1;i<=n;i++) 44 if(!dfn[i]) 45 cnt=0,dfs(0,i); 46 for(int i=1;i<=n;i++) // 输出 割点 47 if(cutnode[i]) 48 printf("%d ",i); 49 printf(" "); 50 for(int i=1;i<=m;i++) // 输出 割边 51 if(cutedge[i]) 52 printf("%d ",i); 53 return 0; 54 }