Tarjan是基於對圖DFS的算法
過程中遇到四種邊
樹枝邊:dfs搜索樹上的邊 滿足邊(u,v) v不在棧中 u為v的父節點
前向邊:與dfs方向一致 祖先指向子孫 沒什么用
后向邊:與dfs方向相反 子孫指向祖先 滿足邊(u,v) v在棧中,u為v的祖先節點
橫叉邊:從某個結點指向搜索樹中另一子樹中某結點的邊 滿足邊(u,v) v在棧中 u不為v的祖先節點
主要過程
我們主要要用到以下數組實現(沒寫鄰接表)
sta[top]數組模擬棧 負責加入和彈出遍歷到的節點 棧的特點:先進后出
dfn[i]表示時間戳 即為搜索次序編號
low[i]初始值=dfn[i] 后續可能更新
co[i]=x 表示 i在第x個強聯通分量中
對於low的更新 我們做如下操作
我們訪問到u,v與u相連
如果(u,v)是樹枝邊 low[u]=min(low[u],low[v])
如果是橫叉邊或后向邊 low[u]=min(dfn[v],low[u])
其實兩種更新的差別就在於v是否被訪問過 即dfn[v]有沒有被賦值
1 void tarjan(int u) 2 { 3 dfn[u]=low[u]=++num;//數據初始為時間戳 4 sta[++top]=u;//訪問到一個點就讓它入棧 5 for(int i=head[u];i;i=nxt[i])//掃描出邊 6 { 7 int v=ver[i]; 8 if(!dfn[v])//沒有被訪問過 那就是樹枝邊 9 { 10 tarjan(v); 11 low[u]=min(low[u],low[v]); 12 } 13 else if(!co[v])//訪問過 並且結點v還在棧內 14 low[u]=min(low[u],dfn[v]); 15 } 16 if(low[u]==dfn[u])//沒有被更新 那么該節點即在棧中以上的結點構成一個強連通分量 17 { 18 co[u]=++tot;//tot記錄強連通分量個數 19 while(sta[top]!=u) 20 { 21 co[sta[top]]=tot;//染色 22 --top; 23 } 24 --top;//u退棧 25 } 26 }
例題bzoj1051: [HAOI2006]受歡迎的牛
題目描述
每一頭牛的願望就是變成一頭最受歡迎的牛。現在有N頭牛,給你M對整數(A,B),表示牛A認為牛B受歡迎。 這
種關系是具有傳遞性的,如果A認為B受歡迎,B認為C受歡迎,那么牛A也認為牛C受歡迎。你的任務是求出有多少頭
牛被所有的牛認為是受歡迎的。
輸入
第一行兩個數N,M。 接下來M行,每行兩個數A,B,意思是A認為B是受歡迎的(給出的信息有可能重復,即有可
能出現多個A,B)
輸出
一個數,即有多少頭牛被所有的牛認為是受歡迎的。
樣例輸入
3 3
1 2
2 1
2 3
1 2
2 1
2 3
樣例輸出
1
提示
100%的數據N<=10000,M<=50000

#include<iostream> #include<cstdio> #include<algorithm> #include<cstdio> using namespace std; const int MaxN=1e4+5,MaxM=5e4+5; int head[MaxN],ver[MaxM],nxt[MaxM],tot; int sta[MaxN],top; int co[MaxN],col; int dfn[MaxN],low[MaxN],num; int si[MaxN],n,m,de[MaxN];; void add(int x,int y) { ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } void tarjan(int u) { dfn[u]=low[u]=++num; sta[++top]=u; for(int i=head[u];i;i=nxt[i]) { int v=ver[i]; if(!dfn[v]){ tarjan(v); low[u]=min(low[v],low[u]); } else if(!co[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ co[u]=++col; ++si[col]; while(sta[top]!=u){ ++si[col]; co[sta[top]]=col; --top; } --top; } } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); add(y,x); } for(int i=1;i<=n;i++){ if(!dfn[i]) tarjan(i);//圖有可能是不連通的 } for(int i=1;i<=n;i++){ for(int j=head[i];j;j=nxt[j]){ if(co[i]!=co[ver[j]]) de[co[ver[j]]]++; } } int ans=0,u=0; for(int i=1;i<=col;i++) if(!de[i]) ans=si[i],u++; if(u==1) printf("%d",ans); else printf("0"); return 0; }