Tarjan算法


Tarjan是基於對圖DFS的算法

過程中遇到四種邊

樹枝邊:dfs搜索樹上的邊  滿足邊(u,v)  v不在棧中 u為v的父節點

前向邊:與dfs方向一致 祖先指向子孫 沒什么用

后向邊:與dfs方向相反 子孫指向祖先 滿足邊(u,v)  v在棧中,u為v的祖先節點

橫叉邊:從某個結點指向搜索樹中另一子樹中某結點的邊 滿足邊(u,v)  v在棧中 u不為v的祖先節點

主要過程

我們主要要用到以下數組實現(沒寫鄰接表)

sta[top]數組模擬棧 負責加入和彈出遍歷到的節點 棧的特點:先進后出

dfn[i]表示時間戳 即為搜索次序編號

low[i]初始值=dfn[i] 后續可能更新

co[i]=x 表示 i在第x個強聯通分量中

對於low的更新 我們做如下操作

我們訪問到u,v與u相連

如果(u,v)是樹枝邊 low[u]=min(low[u],low[v])

如果是橫叉邊或后向邊 low[u]=min(dfn[v],low[u])

其實兩種更新的差別就在於v是否被訪問過 即dfn[v]有沒有被賦值

 1 void tarjan(int u)
 2 {
 3     dfn[u]=low[u]=++num;//數據初始為時間戳
 4     sta[++top]=u;//訪問到一個點就讓它入棧
 5     for(int i=head[u];i;i=nxt[i])//掃描出邊 
 6     {
 7         int v=ver[i];
 8         if(!dfn[v])//沒有被訪問過 那就是樹枝邊 
 9         {
10             tarjan(v);
11             low[u]=min(low[u],low[v]); 
12         }
13         else if(!co[v])//訪問過 並且結點v還在棧內
14             low[u]=min(low[u],dfn[v]); 
15     }
16     if(low[u]==dfn[u])//沒有被更新 那么該節點即在棧中以上的結點構成一個強連通分量
17     {
18         co[u]=++tot;//tot記錄強連通分量個數
19         while(sta[top]!=u) 
20         {
21             co[sta[top]]=tot;//染色 
22             --top;
23         } 
24         --top;//u退棧  
25     }
26 }

例題bzoj1051: [HAOI2006]受歡迎的牛

題目描述

  每一頭牛的願望就是變成一頭最受歡迎的牛。現在有N頭牛,給你M對整數(A,B),表示牛A認為牛B受歡迎。 這
種關系是具有傳遞性的,如果A認為B受歡迎,B認為C受歡迎,那么牛A也認為牛C受歡迎。你的任務是求出有多少頭
牛被所有的牛認為是受歡迎的。

輸入

  第一行兩個數N,M。 接下來M行,每行兩個數A,B,意思是A認為B是受歡迎的(給出的信息有可能重復,即有可
能出現多個A,B)

輸出

  一個數,即有多少頭牛被所有的牛認為是受歡迎的。

樣例輸入

3 3
1 2
2 1
2 3

樣例輸出

1

提示

100%的數據N<=10000,M<=50000
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdio>
using namespace std;
const int MaxN=1e4+5,MaxM=5e4+5;
int head[MaxN],ver[MaxM],nxt[MaxM],tot;
int sta[MaxN],top;
int co[MaxN],col;
int dfn[MaxN],low[MaxN],num;
int si[MaxN],n,m,de[MaxN];;
void add(int x,int y)
{
    ver[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++num;
    sta[++top]=u;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=ver[i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[v],low[u]);
        }
        else if(!co[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        co[u]=++col;
        ++si[col];
        while(sta[top]!=u){
            ++si[col];
            co[sta[top]]=col;
            --top;
        }
        --top;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x,y;i<=m;i++){
        scanf("%d%d",&x,&y);
        add(y,x);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i])
            tarjan(i);//圖有可能是不連通的 
    } 
    for(int i=1;i<=n;i++){
        for(int j=head[i];j;j=nxt[j]){
            if(co[i]!=co[ver[j]])
                de[co[ver[j]]]++;
        }
    } 
    int ans=0,u=0;
    for(int i=1;i<=col;i++)
        if(!de[i])
            ans=si[i],u++;
    if(u==1)
        printf("%d",ans);
    else printf("0");
    return 0;
}
cow

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM