天天看點

Tarjan算法介紹Tarjan算法介紹:

Tarjan算法介紹:

Tarjan算法是圖論中的一種算法,用作于圖的聯通性

  • 它可以做什麼?

    根據 Robert Tarjan 的名字命名的算法Tarjan算法可以線上性時間内求出無向圖的割點與橋,再進一步的求出雙聯通分量,也在資料結構上做出了貢獻。

    Tarjan算法的用途

    1.求橋和割點

    2.求點和邊的雙連通分量

    3.求強連通*

  • 做法基礎

    Tarjan算法基于圖的深度優先周遊上(沒錯!就是與深度優先搜尋(DFS)一樣的東西)

(如果沒學過這樣東西的人可以先收藏一下,等學過了在看)

Targan算法的流程

利用dfs來周遊圖來建構一種數型的結構

Tarjan算法的兩個核心數組

dfn:我們用dfn數組記錄

low:我們用low[i]表示一個節點的子樹中可以到達最小的dfn

(顯然對于一個剛剛周遊到的點我們給他賦上一個新的dfn,low)

‘栗’子來了!

給出一張連通的無向圖G,求出至少加入多少條邊才能使得圖G是一個邊雙連通的。

即求邊雙連通分量把度為一的節點數x (x+1)/2即為答案

注意:求邊雙連通分量時low相同的即為同一組

(标準模闆.cpp)

......
const int M=;
bool map[M][M],vis[M];  
int low[M],dfn[M],cnt[M],num,n,m;
void init(){
    Mt(vis);Mt(map);Mt(low);
    Mt(dfn);Mt(cnt);num=;
}
void dfs(int x,int f){
    dfn[x]=low[x]=++num;
    for(int i=;i<=n;i++){
        if(i!=f&&map[x][i])
            if(!dfn[i]){
                dfs(i,x);
                Min(low[x],low[i]);
            }else Min(low[x],dfn[i]);
    }
}
int main(){
    scanf("%d %d",&n,&m);
    while(m--){
        int x,y;
        scanf("%d%d",&x,&y);
        map[x][y]=map[y][x]=;
    }dfs(,);
    int ans=;
    for(int i=;i<=n;i++)
        for(int j=;j<=n;j++)
            if(map[i][j])
                if(low[i]!=low[j])//注意:求邊雙連通分量時low相同的即為同一組 
                cnt[low[j]]++;
    for(int i=;i<=n;i++)
        if(cnt[i]==)ans++;
    printf("%d",(ans+)/);
    return ;
}
           

給出一個無向圖,你可以在一些點設定一些出口,使得删去任意一個點之後,其他所有的點都至少與一個出口連通,求在出口數量最小情況下的放置出口的方案。

即求點雙連通分量中割點的數量

我們可以用dfs雙連通分量求。因為每個割點一定是雙連通分量中重複部分

(DFS.cpp)

void tarjan(int x,int f){
    dfn[x]=low[x]=++num;
    for(int i=h[x];i;i=nx[i]){
        int y=g[i];
        if(y==f)continue;
        if(!dfn[y]){
            dfs(y,x);
            Min(low[x],low[y]);
            if(dfn[x]<=low[y]){
                if(!f)deg++;
                else cut[x]=;
            }
        }
    }
}
void dfs(int x){
    vis[x]=T;
    if(cut[x])return;
    Cnt++;
    for(int i=h[i];i;i=nx[i]){
        int y=g[i];
        if(cut[y]&&vis[y]!=T)Cut++,vis[y]=T;
        else if(!vis[y])dfs(y);
    }
}
int main(){
    int n=,m,a,b;
    scanf("%d",&m);
    while(m--){
        scanf("%d %d",&a,&b);
        Max(n,a);
        Max(n,b);
        Add(a,b);
        Add(b,a);
    }
    for(int i=;i<=n;i++){
        if(!dfn[i]){
            deg=;
            tarjan(i,);
            if(deg>)cut[i]=;
        }
    }
    for(int i=;i<=n;i++){
        if(!cut[x]&&!vis[x]){
            Cnt=Cut=;
            T++;
            dfs(i);
            if(Cut==)ans1++,ans2*=Cnt;
            if(!Cut)ans1+=,ans2*=Cnt*(Cnt-);
        }
    }printf("%d %d",ans1,ans2);
}
           

tip:tarjantarjan縮點

void tarjan(int x,int f=){
    dfn[x]=low[x]=++Dfn;
    stk[++top]=x;
    for(int i=;i<G[x].size();i++){
        int y=G[x][i];
        if(y==f)continue;
        if(dfn[y])Min(low[x],dfn[y]);
        else{
            tarjan(y,x);
            Min(low[x],low[y]);
            if(low[y]>dfn[x]){
                /*如果不是和x同一塊就開始縮點*/
                ++tree;
                while(low[stk[top]]>dfn[x])T[stk[top--]]=tree;
            }
        }
    }
}
           

END

https://blog.csdn.net/qq_35392050/article/details/62437074 (30%内容取自處)

Ps:本人的第一篇“半原創”,大佬們勿噴,謝謝!

繼續閱讀