tarjan最重要的之一應該就是dfn和low兩個數組吧,這裡判割邊和割點都是根據的這兩個數組
這裡的思路來自李煜東算法競賽進階指南,好像還有一些其他寫法,但是自己比較菜不會......
注意這裡的寫法cnt(邊的編號)要初始化為1
一、割邊
對於割邊的判定,需要:
搜索樹上存在一個x的子節點y要滿足dfn[x]<low[y]
意思就是如果從x的子樹出發,在不經過邊(x,y)的情況下,不能到達比x更早訪問的節點,就說明邊(x,y)是一個割邊
對於無向圖,因為存的時候按照有向圖存的,每一條無向邊一定是成對存的,所以異或1就能變成另一條邊,
若沿著編號為i的邊遞歸進入了節點x,則忽略從x出發編號為i xor 1的邊,通過其它邊計算low[x](書上抄的)
#include<iostream> #include<cstdio> using namespace std; const int maxn=100010; int n,m,num; int head[maxn],cnt=1;//cnt初始化為1? struct node{ int v,nxt; }e[maxn*2]; int dfn[maxn],low[maxn]; bool bridge[maxn*2]; void add(int u,int v){ e[++cnt].v=v; e[cnt].nxt=head[u]; head[u]=cnt; } void tarjan(int x,int fa){ dfn[x]=low[x]=++num; for(int i=head[x];i;i=e[i].nxt){ int y=e[i].v; if(!dfn[y]){ tarjan(y,i); low[x]=min(low[x],low[y]); if(low[y]>dfn[x])bridge[i]=bridge[i^1]=1; //異或是為了處理無向邊,每對無相邊一定是異或的關係 } else if(i!=(fa^1)) low[x]=min(low[x],dfn[y]); } } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); add(x,y);add(y,x); } for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,0); for(int i=2;i<cnt;i+=2) if(bridge[i])printf("%d %d ",e[i^1].v,e[i].v); }
二、割點
對於割點的判定,需要:
若x不是搜索樹的根節點,則搜索樹上x的一個子節點y要滿足dfn[x]<=low[y]
特別的,如果x是搜索樹的根節點,則x是割點當且僅當搜索樹上存在至少兩個子節點y1,y2滿足上述條件
證明方法遇割邊類似
因為判定條件是小於等於號,所以不必考慮父節點和重邊的問題
#include<iostream> #include<cstdio> using namespace std; const int maxn=100010; int n,m,num,root,sum; int head[maxn],cnt=1; int dfn[maxn],low[maxn],st[maxn]; struct node{ int v,nxt; }e[maxn*2]; void add(int u,int v){ e[++cnt].v=v; e[cnt].nxt=head[u]; head[u]=cnt; } bool cut[maxn]; void tarjan(int x) { dfn[x]=low[x]=++num; int fl=0;//兒子個數 for(int i=head[x];i;i=e[i].nxt){ int y=e[i].v; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]){//割點判定條件 fl++; if(x!=root || fl>1)cut[x]=1;//根節點必須保證至少子樹有兩個節點 } } else low[x]=min(low[x],dfn[y]); } } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); if(x==y)continue;//自環 add(x,y);add(y,x); } for(int i=1;i<=n;i++) if(!dfn[i])root=i,tarjan(i); for(int i=1;i<=n;i++)if(cut[i])sum++;//必須最後再數 printf("%d ",sum); for(int i=1;i<=n;i++) if(cut[i])printf("%d ",i); }