一.算法簡介node
Tarjan 算法一種由Robert Tarjan提出的求解有向圖強連通份量的算法,它能作到線性時間的複雜度。算法
咱們定義:ide
若是兩個頂點能夠相互通達,則稱兩個頂點強連通(strongly connected)。若是有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。有向圖的極大強連通子圖,稱爲強連通份量(strongly connected components)。學習
例如:在上圖中,{1 , 2 , 3 , 4 } , { 5 } , { 6 } 三個區域能夠相互連通,稱爲這個圖的強連通份量。spa
Tarjan算法是基於對圖深度優先搜索的算法,每一個強連通份量爲搜索樹中的一棵子樹。搜索時,把當前搜索樹中未處理的節點加入一個堆棧,回溯時能夠判斷棧頂到棧中的節點是否爲一個強連通份量。.net
再Tarjan算法中,有以下定義。3d
DFN[ i ] : 在DFS中該節點被搜索的次序(時間戳)code
LOW[ i ] : 爲i或i的子樹可以追溯到的最先的棧中節點的次序號component
當DFN[ i ]==LOW[ i ]時,爲i或i的子樹能夠構成一個強連通份量。blog
二.算法圖示
以1爲Tarjan 算法的起始點,如圖
順次DFS搜到節點6
回溯時發現LOW[ 5 ]==DFN[ 5 ] , LOW[ 6 ]==DFN[ 6 ] ,則{ 5 } , { 6 } 爲兩個強連通份量。回溯至3節點,拓展節點4.
拓展節點1 , 發現1再棧中更新LOW[ 4 ],LOW[ 3 ] 的值爲1
回溯節點1,拓展節點2
自此,Tarjan Algorithm 結束,{1 , 2 , 3 , 4 } , { 5 } , { 6 } 爲圖中的三個強連通份量。
不難發現,Tarjan Algorithm 的時間複雜度爲O(E+V).
模板:
1 void dfs(int u) 2 { 3 times++;//記錄dfn順序 4 dfn[u]=times;//賦值 5 low[u]=times;//先賦初值 6 vis[u]=true;//vis[i]用來判斷i是否搜索過; 7 insta[u]=true;//表示是否在棧中,true爲在棧中; 8 stack[top]=u;//棧頂 9 top++; 10 for(int i=head[u];i!=-1;i=edge[i].next)// 以建圖順序枚舉此點所連的邊 11 { 12 int v=edge[i].to;//搜索到的點 13 if(!vis[v])//若是未搜索過即未入棧 14 { 15 dfs(v);//繼續以此點進行深搜 16 low[u]=min(low[u],low[v]);//更新low值,此邊爲樹枝邊因此比較u此時的 17 } // low值(未更新時就是其dfn值)和v的low值 18 else 19 if(insta[v]==true)//若是搜索過且在棧中,說明此邊爲後向邊或棧中橫叉邊 20 { 21 low[u]=min(low[u],dfn[v]);//更新low值,比較u此時的low值和v的dfn值 22 } 23 } 24 25 if(low[u]==dfn[u])//相等說明找到一個強連通份量 26 { 27 while(top>0&&stack[top]!=u)//開始退棧一直退到 u爲止 28 { 29 top--; 30 insta[stack[top]]=false; 31 } 32 } 33 }
例題:
題目大意:題目大意是:在一個牧羣中,有N個奶牛,給定M對關係(A,B)表示A仰慕B,並且仰慕關係有傳遞性,問被全部奶牛(除了本身)仰慕的奶牛個數
解題思路:找出全部的連通份量,若是隻有一個連通份量的出度爲0,那麼輸出那個連通份量的點的個數便可,若是不惟一就輸出0
由於連通份量的出度有兩個的話,那麼確定至少存在一頭牛不仰慕另一頭牛,因此咱們至少保證要出度爲0的連通份量惟一
解題思路:
一、用Tarjan求雙連通份量而後縮成點。這些點會造成一棵樹。
二、求樹上的節點有多少個出度爲零,若是有一個就輸出那個點裏包含的全部點(由於是縮點出來的樹)。
注意:
一、給出的圖會有不連通的可能,若是那樣確定輸出零。由於不連通確定不會有全部其餘牛認爲某隻牛很牛的狀況出現。
二、若是縮點後有多個出度爲零的點,那麼輸出零。由於這樣圖雖然聯通了,可是仍是不會出現全部其餘牛認爲某隻牛很牛的狀況(本身畫一下就知道啦)。
#include <stdio.h> #include <string.h> const int MAXN = 10005; const int MAXM = 100005; struct node { int to,next; } edge[MAXM]; int n,m,head[MAXN],dfn[MAXN],low[MAXN],stack1[MAXN],num[MAXN],du[MAXN],vis[MAXN],cnt,time,top,cut; void init() { memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num)); memset(du,0,sizeof(du)); cnt=0; time=1; top=0; cut=0; } void addedge(int u,int v) { edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; cnt++; } int min(int a,int b) { if(a>b)a=b; return a; } void dfs(int u,int fa) { dfn[u]=time; low[u]=time; time++; vis[u]=1; stack1[top]=u; top++; for(int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].to; if(!vis[v]) { dfs(v,u); low[u]=min(low[u],low[v]); } else if(vis[v]) { low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]) { cut++; while(top>0&&stack1[top]!=u) { top--; vis[stack1[top]]=2; num[stack1[top]]=cut; } } } int main() { int i,u,v; while(scanf("%d%d",&n,&m)!=EOF) { init(); for(i=0; i<m; i++) { scanf("%d%d",&u,&v); addedge(u,v); } for(int i=1; i<=n; i++) { if(!vis[i]) { dfs(i,0); } } for(i=1; i<=n; i++) { for(int j=head[i]; j!=-1; j=edge[j].next) { if(num[i]!=num[edge[j].to]) { du[num[i]]++; } } } int sum=0,x; for(i=1; i<=cut; i++) { if(!du[i]) { sum++; x=i; } } if(sum==1) { sum=0; for(i=1; i<=n; i++) { if(num[i]==x) { sum++; } } printf("%d\n",sum); } else { puts("0"); } } return 0; }