眾所周知,tarjan是個非常nb的人,他發明了很多nb的演算法,tarjan演算法就是其中乙個,它常用於求解強連通分量,割點和橋等。雖然具體實現的細節不太一樣,但是大體思路是差不多的。先來說一下大體思路。
強連通分量,縮點
我們先來定義幾個東西
時間戳:在搜尋樹中被遍歷到的次序
比如在下圖中
每個節點按照遍歷順序編的號就是它的時間戳
dfn[i]:表示第i個點的時間戳
low[i]:表示點i及i的子樹所能追溯到的最早的節點的時間戳
low陣列看起來很難理解是不是?
先來看一張非常經典的圖
我們發現對於結點1,3,2,4,它們的low值都是1。為什麼呢?因為這些點都直接或者間接的能夠追溯到的最早的點1,而點1的dfn值為1,所以這些點的low值自然也就是1了
我們可以通過手算發現圖中有三個強連通分量:,,
我們發現,每乙個連通分量都有乙個點(以下稱為代表點)的low值=dfn值,也就是說這個點及它的子樹所能到達的最早的點就是他自己。
於是可以知道,對於dfn=low的點就是這乙個強連通分量的代表點
那麼要求強連通分量,實際上就是求有多少個點的low=dfn
用乙個棧來實現,尋找low時只在棧裡面找,彈出時不斷從棧頂彈出直到彈出這個點
**:
int dfn[50005],low[50005來看幾道例題:];//
dfn表示時間戳
//low表示點i及i的子樹所能追溯到的最早的節點的時間戳
intind;
//ind表示遍歷順序
intin[50005],s[50005
],top;
//in表示當前這個點是否在佇列中
//s是模擬的棧
//top是棧頂
intcnt_scc;
//強連通分量的個數
int scc[50005],cntscc[50005
];//
scc表示每乙個點屬於哪乙個強連通分量
//cntscc表示強連通分量的大小
void tarjan(int
x)
else}}
if(dfn[x]==low[x])//
如果找到強連通分量的代表點 }}
如果有環,意味著這個環裡的牛都互相喜歡
我們可以先求出環,然後把每乙個環都看作乙個點,這樣整個圖就變成了乙個dag(有向無環圖)
看有幾個點出度為0,如果大於乙個點沒有出邊,就說明沒有最受歡迎的牛,因為必定有一對牛相互不服
如果只有乙個,那麼強聯通分量的大小就是答案
**:
#includeusingp2002 訊息傳遞namespace
std;
intn,m;
int cnt,head[50000
];struct
edge
edg[
50005
];inline
void add(int
from,int
to)int dfn[50005],low[50005],ind,in[50005
];int s[50005
],top;
intcnt_scc;
int scc[50005],cntscc[50005
];void tarjan(int
x)
else}}
if(dfn[x]==low[x])
}}int
out[50005
];int
ans;
intmain()
for(int i=1;i<=n;i++)
for(int i=1;i<=n;i++)
}for(int i=1;i<=cnt_scc;i++)}}
cout
<
}
我們發現如果這個題有環,那麼不論在這個環上哪乙個點開始傳遞資訊,這個環中其他的點都可以到達,那麼可以用tarjan把環縮成點。為了使每乙個點都能被傳遞到,只需要找到所有入度為0的點,在這些點上開始傳遞資訊就好了
**:
#includeusing類似的題還有洛谷1262,這裡就先不說了namespace
std;
intn,m;
int head[100005
],cnt;
struct
edge
edg[
500005
];inline
void add(int
from,int
to)
int low[100005],dfn[100005
],ind;
int s[100005
],top;
bool
in[100005
];int scc[100005
],cnt_scc;
inline
void tarjan(int
x)
else
}if(low[x]==dfn[x])
}}int
ans;
int gin[100005
];int
main()
for(int i=1;i<=n;i++)
for(int i=1;i<=n;i++)}}
for(int i=1;i<=cnt_scc;i++)
cout
<
}
tarjan求割點
什麼是割點?
給你一張連通圖,在上面找乙個點,如果去掉這個點和所有連著它的邊,整個圖就不能保持連通,那麼這個點就是割點
比如這張圖,裡面的割點有1,4,5
怎麼求割點?
首先選定乙個dfs樹的樹根,從這個點開始遍歷整張圖。
對於根節點,判斷是不是割點顯然只需要看他的子樹的個數是不是大於等於2
對於非根節點x,如果存在兒子節點y,使得dfn[x]<=low[y],則x一定是割點。
顯然如果x的所有兒子能夠不經過x直接到達他的祖先,這個點就一定不是割點;反之,則說明去掉它一定會改變圖的連通性
**:
int low[20005],dfn[20005例:**:],ind,ans;
bool cut[20005
];inline
void tarjan(int x,int
fa)
else
}if(x==fa&&ch>=2) cut[fa]=1
;}
#includeusingnamespace
std;
intn,m;
int head[20005
],cnt;
struct
edge
edg[
200005
];inline
void add(int
from,int
to)
int low[20005],dfn[20005
],ind,ans;
bool cut[20005
];inline
void tarjan(int x,int
fa)
else
}if(x==fa&&ch>=2) cut[fa]=1;}
intmain()
for(int i=1;i<=n;i++)
for(int i=1;i<=n;i++)
cout
for(int i=1;i<=n;i++)
}
關於tarjan演算法的一些整理
要noip了,來補一些演算法。當然馬蜂也都是很久以前的了,不喜勿噴。定義 顯然僅在有向圖中有 乙個極大子圖滿足內部任意一點出發可到其他任一點。可以用來縮點。code 一些地方有注釋。注意僅有這個 是沒fa的。void tarjan int u if low u dfn u top 定義 這個順便是有...
tarjan演算法詳解
參考 tarjan演算法在強連通分量分離中運用很廣,書寫簡單,並且可以拓展到圖的割點,割邊上,十分強大 具體思路 令dfn u 表示當前點的時間戳 low u 表示當前點所能到達的點的時間戳中最小的乙個 到達點u時,將其入棧 拓展點u後代 當且僅當dfn u low u 時,棧頂元素全部出棧,此時出...
Tarjan 演算法筆記
tarjan演算法 tarjan演算法屬於圖論中的乙個演算法,主要用來求乙個圖中的強連通分量,之後就可以做很多事,比如說縮點 求雙聯通分支等。強連通 在乙個有向圖中,對於幾個點,如果它們能夠互相到達,那麼稱它們強連通。強連通分量 可以這樣理解 把乙個圖里的點分成幾坨,每坨中的點都能夠互相到達 他們強...