在有向圖中,如果2個頂點之間存在至少一條路徑,則稱這2個頂點強連通。如果有向圖G中任意2個頂點都強連通,則稱G是一個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量。
強連通分量的求法分為主流的2種,一種是Kosaraju,做2次DFS。另外一種就是偉大的計算機科學家Tarjan發明的算法,該算法只需要做一次DFS即可,比Kosaraju更快。
網上關於Tarjan算法的介紹很多,我推薦Byvoid大牛寫的:
有向圖強連通分量的Tarjan算法
這篇文章被wiki推薦,非常經典,看完秒懂
一.如何求強連通分量
下面我們先來做一道模板題:
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求強連通分量的方法,不過對於一些貢獻具有傳導性的問題有時候需要縮點,比如友情傳遞、路上權值等。
縮點的思想也很顯然,因為強連通分量中的每兩個點都是強連通的,故可以將一個強連通分量當做一個超級點,而點權按題意來定。
如下圖所示就是一個縮點的例子

同樣關於縮點問題,我們來看一道簡單的模板題
BZOJ1051 --- [HAOI2006]受歡迎的牛
分析:首先對於一個強連通分量里面的所有點都滿足條件,於是我們對圖進行縮點,這樣我們得到的所有點都不是強連通的,現在整張圖就是一個DAG。我們考慮出度為0的點,則在圖中至少存在一個出度為0的點,如果超過1個,則必不可能滿足條件,否則這個點就滿足條件。這個點很可能為所有強連通分量構成的超級點,所以也就是要求的也就是這個強連通分量中點的個數
#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;
}
