1. 程式人生 > >【算法】Tarjan算法求強連通分量

【算法】Tarjan算法求強連通分量

標記 我們 else if show tar ans stk oid 入棧

概念:

  • 在有向圖G中,如果兩個定點u可以到達v,並且v也可以到達u,那麽我們稱這兩個定點強連通。
  • 如果有向圖G的任意兩個頂點都是強連通的,那麽我們稱G是一個強連通圖。
  • 一個有向圖中的最大強連通子圖,稱為強連通分量。

tarjan的主要思想:

從一個點開始DFS,記錄兩個數組,dfn[]和low[]。

其中,dfn[i]指的是到達第i個點的時間。

low[i]指第i個點直接或間接可到達的點中的最小dfn[j]。

low[i]數組的初始值為dfn[i]。

舉個例子,如圖所示:

假如我們從第一個點開始跑DFS,dfn[1]=1,low[1]=1;那麽走到第2個點時,標記dfn[2]=2;第三個點dfn[3]=2;dfn[4]=3;dfn[5]=3;dfn[6]=4;

同時,我們也可以得到另一個數組low[]:

low[1]=1;low[2]=1;low[3]=1;low[4]=1;low[5]=3;low[6]=4;

推廣:

對於每一個沒有被遍歷到的點u,如果從當前點有一條到未遍歷點u的有向邊,則遍歷到u,同時將點u入棧,時間戳+1並用dfn[u]記錄到達點u的時間,枚舉從u發出的每一條邊,如果該邊指向的點沒有被訪問過,那麽繼續dfs,回溯後low[u]=min(low[u],low[v])(其中v為u可以到達的點。)如果該點已經訪問過並且該點仍在棧裏,那麽low[u]=min(low[u],dfn[v])。

證明:

略,作為一名OIer,知道結論就行了!!!

代碼:

void tarjan(int u)
{
  low[u]=dfn[u]=++tim;
  instack[u]=1;stk[top++]=u;
  for(int i=H[u];i;i=X[i])
  {
    int v=P[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])
  {
    cnt++;
    int k;
    do
    {
      siz[cnt]++;
      k=stk[--top];
      belong[k]=cnt;
      instack[k]=0;
    }
    while(k!=u);
  }
}

  

例題:

迷宮城堡

Summer Holiday

Instantaneous Transference

寶藏

【算法】Tarjan算法求強連通分量