1. 程式人生 > >Tarjan演算法求割點 附裸題

Tarjan演算法求割點 附裸題

無向圖中割點的概念:
割點:一個結點稱為割點(或者割頂)當且僅當去掉該節點及其相關的邊之後的子圖不連通。
判斷一個結點是否為割點有如下兩個條件:
條件一:若該結點u為根結點,它應有兩個及兩個以上的子結點。
條件二:若該結點u非根結點,它應有一個滿足條件的子結點v:該子結點v及其後代沒有反向邊可以連回結點u的祖先。
在Tarjan演算法中,有兩個points非常重要:
1.dfn[i]表示結點i被訪問的時間戳,即深度優先訪問次序。
2.low[i]表示結點i及其子樹的結點通過反向邊能到達的結點的最小dfn值。

下面貼一個求割點的模板

#include<iostream>
#include<cstdio> #include<cstring> #include<algorithm> #define maxn 10005 using namespace std; int n,m,x,y,root; int tot; //邊的序號 int id; //深度優先數即遍歷次序 int Next[maxn],Head[maxn],Dst[maxn]; bool iscut[maxn]; int low[maxn],dfn[maxn]; void init() { tot=0;id=0; memset(iscut,0,sizeof(iscut)); memset
(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); } void add_edge(int x,int y) { tot++; Next[tot]=Head[x]; Head[x]=tot; Dst[tot]=y; } void dfs(int x,int pre) { int child=0; low[x]=dfn[x]=++id; for(int i=Head[x];i;i=Next[i]) { int y=Dst[i]; if(!dfn[y]) { dfs(y,x); low[x]=min(low[x],low[y]); if
(x==root) child++; if(x==root&&child>=2) //滿足條件一 iscut[root]=true; if(x!=root&&low[y]>=dfn[x]) //滿足條件二 iscut[x]=true; } else if(y!=pre)//結點y在結點x之前已經訪問過了,且y不是x的父親結點,必有dfn[y]<dfn[x] low[x]=min(low[x],dfn[y]); } } int main() { init(); scanf("%d%d",&n,&m); for(int i=0;i<m;i++) { scanf("%d%d",&x,&y); add_edge(x,y); add_edge(y,x); } for(int i=1;i<=n;i++) if(!dfn[i]) { root=i; dfs(i,i); } printf("輸出割點:\n"); for(int i=1;i<=n;i++) if(iscut[i]) printf("%d ",i); printf("\n"); return 0; }

其中low[x]=min(low[x],dfn[y])這個地方,起初讓我很不理解,為什麼不可以是low[x]=min(low[x],low[y])呢?
再讀一遍第二個point:low[i]表示 結點i及其子樹的結點 通過反向邊 能到達的結點的最小dfn值。
因為y不是x的子結點,因此這裡只能用low[x]=min(low[x],dfn[y])