tarjan演算法,一個關於 圖的聯通性的神奇演算法

來源:互聯網
上載者:User

標籤:get   tle   strong   ext   lap   tarjan   min   can   互連   

一.演算法簡介

Tarjan 演算法一種由Robert Tarjan提出的求解有向圖強連通分量的演算法,它能做到線性時間的複雜度。

 

我們定義:

如果兩個頂點可以相互連達,則稱兩個頂點強連通(strongly connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。有向圖的極大強連通子圖,稱為強連通分量(strongly connected components)。

例如:在中,{1 , 2 , 3 , 4 } , { 5 } ,  { 6 } 三個地區可以相互連通,稱為這個圖的強連通分量。

Tarjan演算法是基於對圖深度優先搜尋的演算法,每個強連通分量為搜尋樹中的一棵子樹。搜尋時,把當前搜尋樹中未處理的節點加入一個堆棧,回溯時可以判斷棧頂到棧中的節點是否為一個強連通分量。

再Tarjan演算法中,有如下定義。

DFN[ i ] : 在DFS中該節點被搜尋的次序(時間戳記)

LOW[ i ] : 為i或i的子樹能夠追溯到的最早的棧中節點的序號

當DFN[ i ]==LOW[ i ]時,為i或i的子樹可以構成一個強連通分量。

 

二.演算法圖示

以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 }
View Code

 例題:

poj2186 - Popular Cows

題目大意:題目大意是:在一個牧群中,有N個奶牛,給定M對關係(A,B)表示A仰慕B,而且仰慕關係有傳遞性,問被所有奶牛(除了自己)仰慕的奶牛個數

解題思路:找出所有的連通分量,如果只有一個連通分量的出度為0,那麼輸出那個連通分量的點的個數即可,如果不唯一就輸出0
因為連通分量的出度有兩個的話,那麼肯定至少存在一頭牛不仰慕另外一頭牛,所以我們至少保證要出度為0的連通分量唯一

解題思路:

1、用Tarjan求雙連通分量然後縮成點。這些點會形成一棵樹。

2、求樹上的節點有多少個出度為零,如果有一個就輸出那個點裡包含的所有點(因為是縮點出來的樹)。

 

注意:

1、給出的圖會有不連通的可能,如果那樣肯定輸出零。因為不連通肯定不會有所有其他牛認為某隻牛很牛的情況出現。

2、如果縮點後有多個出度為零的點,那麼輸出零。因為這樣圖雖然聯通了,但是還是不會出現所有其他牛認為某隻牛很牛的情況(自己畫一下就知道啦)。

求強連通分量主要是為了簡化圖的構造,如果分量外的一個點能到達分量內的其中一個點,那麼它必定能到達分量內的所有點,所以某種程度上,強連通分量可以簡化成一個點。
#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;}
View Code

 

tarjan演算法,一個關於 圖的聯通性的神奇演算法

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.