在有向圖中,若是2個頂點之間存在至少一條路徑,則稱這2個頂點強連通。若是有向圖G中任意2個頂點都強連通,則稱G是一個強連通圖。非強連通圖有向圖的極大強連通子圖,稱爲強連通份量。 強連通份量的求法分爲主流的2種,一種是Kosaraju,作2次DFS。另一種就是偉大的計算機科學家Tarjan發明的算法,該算法只須要作一次DFS便可,比Kosaraju更快。 網上關於Tarjan算法的介紹不少,我推薦Byvoid大牛寫的: 有向圖強連通份量的Tarjan算法 這篇文章被wiki推薦,很是經典,看完秒懂php
一.如何求強連通份量
下面咱們先來作一道模板題:c++
HDU1269 --- 迷宮城堡 分析:Tarjan模板題,看整張圖是否是一張強連通圖算法
#include "bits/stdc++.h" using namespace std; const int maxn=1e5+100; struct Edge{ int to,next; }edge[maxn]; int n,m,tot,head[maxn]; int low[maxn],dfn[maxn],num[maxn],s[maxn],belong[maxn]; bool Instack[maxn]; int Index,scc,top; void init(){ tot=0; memset(head,-1,sizeof(head)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(num,0,sizeof(num)); memset(s,0,sizeof(s)); memset(belong,0,sizeof(belong)); memset(Instack,false,sizeof(Instack)); Index=scc=top=0; } void add(int u,int v){ edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++; } void Tarjan(int u){ int v; dfn[u]=low[u]=++Index; s[top++]=u; Instack[u]=true; for(int i=head[u];i!=-1;i=edge[i].next){ v=edge[i].to; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); }else if(Instack[v]){ low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]){ scc++; do{ v=s[--top]; Instack[v]=false; belong[v]=scc; num[scc]++; }while(u!=v); } } int main() { while(scanf("%d%d",&n,&m)!=EOF){ if(n==0&&m==0) break; init(); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); } for(int i=1;i<=n;i++){ if(!dfn[i]) Tarjan(i); } if(scc==1) printf("Yes\n"); else printf("No\n"); } return 0; }
二.Tarjan縮點
其實縮點也是運用Tarjan求強連通份量的方法,不過對於一些貢獻具備傳導性的問題有時候須要縮點,好比友情傳遞、路上權值等。 縮點的思想也很顯然,由於強連通份量中的每兩個點都是強連通的,故能夠將一個強連通份量當作一個超級點,而點權按題意來定。 以下圖所示就是一個縮點的例子 spa
一樣關於縮點問題,咱們來看一道簡單的模板題code
BZOJ1051 --- [HAOI2006]受歡迎的牛 分析:首先對於一個強連通份量裏面的全部點都知足條件,因而咱們對圖進行縮點,這樣咱們獲得的全部點都不是強連通的,如今整張圖就是一個DAG。咱們考慮出度爲0的點,則在圖中至少存在一個出度爲0的點,若是超過1個,則必不可能知足條件,不然這個點就知足條件。這個點極可能爲全部強連通份量構成的超級點,因此也就是要求的也就是這個強連通份量中點的個數blog
#include "bits/stdc++.h" using namespace std; const int maxn=5e4+10; vector<int>g[maxn]; int n,m; int low[maxn],dfn[maxn],s[maxn],num[maxn],belong[maxn]; bool Instack[maxn]; int Index,scc,top; void Tarjan(int u){ int v; low[u]=dfn[u]=++Index; s[top++]=u; Instack[u]=true; for(int i=0;i<g[u].size();i++){ v=g[u][i]; if(!dfn[v]){ Tarjan(v); low[u]=min(low[u],low[v]); }else if(Instack[v]){ low[u]=min(low[u],dfn[v]); } } if(low[u]==dfn[u]){ ++scc; do{ v=s[--top]; Instack[v]=false; belong[v]=scc; num[scc]++; }while(u!=v); } } int cnt[maxn],du[maxn]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); g[x].push_back(y); } for(int i=1;i<=n;i++){ if(!dfn[i]) Tarjan(i); } for(int i=1;i<=n;i++){ for(int j=0;j<g[i].size();j++){ int v=g[i][j]; if(belong[i]!=belong[v]){ du[belong[i]]++; } } cnt[belong[i]]++; } int tmp=0,ans=0; for(int i=1;i<=scc;i++){ if(du[i]==0){ tmp++; ans=cnt[i]; } } if(tmp>1) printf("0\n"); else printf("%d\n",ans); return 0; }