有向圖的強連通份量

在有向圖中,若是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求強連通份量的方法,不過對於一些貢獻具備傳導性的問題有時候須要縮點,好比友情傳遞、路上權值等。 縮點的思想也很顯然,由於強連通份量中的每兩個點都是強連通的,故能夠將一個強連通份量當作一個超級點,而點權按題意來定。 以下圖所示就是一個縮點的例子 1.pngspa

一樣關於縮點問題,咱們來看一道簡單的模板題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;
}
相關文章
相關標籤/搜索