1. 程式人生 > >初學強連通演算法--Tarjan

初學強連通演算法--Tarjan

Tarjan演算法的詳細解釋見上面兩個部落格。。。。。

我們討論一下Tarjan演算法能夠幹一些什麼: 既然我們知道,Tarjan演算法相當於在一個有向圖中找有向環,那麼我們Tarjan演算法最直接的能力就是縮點辣!縮點基於一種染色實現,我們在Dfs的過程中,嘗試把屬於同一個強連通分量的點都染成一個顏色,那麼同一個顏色的點,就相當於一個點。比如剛才的例項圖中縮點之後就可以變成這樣: 將一個有向帶環圖變成了一個有向無環圖(DAG圖)。很多演算法要基於有向無環圖才能進行的演算法就需要使用Tarjan演算法實現染色縮點,建一個DAG圖然後再進行演算法處理。在這種場合,Tarjan演算法就有了很大的用武之地辣!

那麼這個時候 ,我們再引入一個數組color【i】表示節點i的顏色,再引入一個數組stack【】實現一個棧,然後在Dfs過程中每一次遇到點都將點入棧,在每一次遇到關鍵點的時候將棧內元素彈出,一直彈到棧頂元素是關鍵點的時候為止,對這些彈出來的元素進行染色即可。

void Tarjan(int u)//此程式碼僅供參考
{
    vis[u]=1;
    low[u]=dfn[u]=cnt++;
    stack[++tt]=u;
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(vis[v]==0)Tarjan(v);
        if(vis[v]==1)low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u])
    {
        sig++;
        do
        {
            low[stack[tt]]=sig;
            color[stack[tt]]=sig;
            vis[stack[tt]]=-1;
        }
        while(stack[tt--]!=u);
    }
}

上一道例題:

The Bottom of a Graph Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 12220 Accepted: 5012 Description

We will use the following (standard) definitions from graph theory. Let V be a nonempty and finite set, its elements being called vertices (or nodes). Let E be a subset of the Cartesian product V×V, its elements being called edges. Then G=(V,E) is called a directed graph. Let n be a positive integer, and let p=(e1,…,en) be a sequence of length n of edges ei∈E such that ei=(vi,vi+1) for a sequence of vertices (v1,…,vn+1). Then p is called a path from vertex v1 to vertex vn+1 in G and we say that vn+1 is reachable from v1, writing (v1→vn+1). Here are some new definitions. A node v in a graph G=(V,E) is called a sink, if for every node w in G that is reachable from v, v is also reachable from w. The bottom of a graph is the subset of all nodes that are sinks, i.e., bottom(G)={v∈V|∀w∈V:(v→w)⇒(w→v)}. You have to calculate the bottom of certain graphs. Input

The input contains several test cases, each of which corresponds to a directed graph G. Each test case starts with an integer number v, denoting the number of vertices of G=(V,E), where the vertices will be identified by the integer numbers in the set V={1,…,v}. You may assume that 1<=v<=5000. That is followed by a non-negative integer e and, thereafter, e pairs of vertex identifiers v1,w1,…,ve,we with the meaning that (vi,wi)∈E. There are no edges other than specified by these pairs. The last test case is followed by a zero. Output

For each test case output the bottom of the specified graph on a single line. To this end, print the numbers of all nodes that are sinks in sorted order separated by a single space character. If the bottom is empty, print an empty line. Sample Input

3 3 1 3 2 3 3 1 2 1 1 2 0 Sample Output

1 3 2

題目大意:給你一堆點,一堆邊,讓你找到縮點之後出度為0的節點, 然後將節點編號從小到大排序輸出。

思路:Tarjan,縮點染色,判斷出度為0的強連通分量,將整個集合排序,輸出即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N = 1e6 + 5;

vector<int>ve[N];
int ans[N];
int deg[N];
int color[N],vis[N],dfn[N],low[N];
int n,m,t,cnt,sig;
//模擬棧
int sta[N];

void init()
{
    for(int i = 1;i <= n;++i) ve[i].clear();
    memset(deg,0,sizeof(deg));
    memset(color,0,sizeof(color));
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
}

void Tarjan(int u)
{
    vis[u] = 1;
    dfn[u] = low[u] = cnt++;
    sta[++t] = u;
    int len = ve[u].size();
    for(int i = 0;i < len;++i){
        int v = ve[u][i];
        if(vis[v] == 0) Tarjan(v);
        if(vis[v] == 1) low[u] = min(low[u],low[v]);
    }
    if(dfn[u] == low[u]){
        sig++;
        do{
            color[sta[t]] = sig;
            vis[sta[t]] = 1;
        }while(sta[t--] != u);
    }
}

void solve()
{
    t = -1;cnt = 1;sig = 0;
    for(int i = 1;i <= n;++i){
        if(vis[i] == 0){
            Tarjan(i);
        }
    }
    for(int i = 1;i <= n;++i){
        int len = ve[i].size();
        for(int j = 0;j < len;++j){
            int v = ve[i][j];
            if(color[i] != color[v]){
                deg[color[i]]++;
            }
        }
    }
    int pp = 0;
    for(int i = 1;i <= sig;++i){
        if(deg[i] > 0) continue;
        for(int j = 1;j <= n;++j){
            if(color[j] == i){
                ans[pp++] = j;
            }
        }
    }
    sort(ans,ans + pp);
    for(int i = 0;i < pp;++i){
        if(i == pp - 1) printf("%d\n",ans[i]);
        else printf("%d ",ans[i]);
    }
}

int main()
{
    while(~scanf("%d",&n))
    {
        if(n == 0) break;
        scanf("%d",&m);
        init();
        for(int i = 0;i < m;++i){
            int x,y;
            scanf("%d %d",&x,&y);
            ve[x].pb(y);
        }
        solve();
    }
    return 0;
}