雖然是複習,但還是學到許多。
過程中遇到四種邊
1>樹枝邊:\(dfs\)搜尋樹上的邊 滿足邊\((u,v) ,v\)不在棧中 \(u\)為\(v\)的父節點
2>前向邊:與\(dfs\)方向一致 祖先指向子孫 沒什麼用
3>後向邊:與\(dfs\)方向相反 子孫指向祖先 滿足邊\((u,v)\). \(v\)在棧中,\(u\)為\(v\)的祖先節點
4>橫叉邊:從某個結點指向搜尋樹中另一子樹中某結點的邊 滿足邊\((u,v)\), \(v\)在棧中 \(u\)不為\(v\)的祖先節點
定義兩個陣列\(dfn\)和\(low\),\(dfn[x]\)表示\(x\)節點是第幾個被遍歷到的。\(low[x]\)表示\(x
x\)以及它的所有子樹的出邊的\(dfn\)的最小值,由定義可以得出:
如果\((u,v)\)是樹枝邊 \(low[u]=min(low[u],low[v])\)
如果是橫叉邊或後向邊 \(low[u]=min(dfn[v],low[u])\)
當結點\(u\)的搜尋過程結束之後,若\(dfn[u] == low[u]\) 則以\(u\)為根的搜尋子樹上所有還在棧中的結點,是乙個強連通分量,可退棧,為什麼呢????
我也不知道
通俗的說,若\(u\)為強連通分量的根,那麼它的子孫中的最高祖先應該是它本身。
陣列的初始化,當首次到達\(u\)這個點時,更新\(low\)與\(dfn\)的值, 將\(u\)入棧
更新\(low[u]\)
如果\((u,v)\)是樹枝邊 \(low[u]=min(low[u],low[v])\)
如果是橫叉邊或後向邊 \(low[u]=min(dfn[v],low[u])\)
如果u的子樹全被遍歷完,\(low[u] == dfn[u]\)那麼退棧,退到\(u\)退出,這些退出的元素就是乙個強連通分量。
void tarjan(int x)
if (dfn[x] == low[x])
}}for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
其實就是縮點,要不然也沒別的用處。
因為每乙個強連通分量中的點都是相互可達的,我們可以將當前這個強連通分量縮成乙個點,這個點的權值由題目來定(通常會有強連通分量中的點權和或者點權的最小值)
我們用到染色的思想,因為退棧的時候退棧的所有元素都是同乙個強連通分量中的,所以我們可以在這個時將所有的點都染成同一種顏色,同時處理縮完點之後點的權值。
code
void tarjan(int x)
if (dfn[x] == low[x]) }}
例題
間諜網路
穩定婚姻
訊息擴散
hxy燒情侶
受歡迎的牛
校園網最大半連通子圖
網路協議
訊息的傳遞
間諜網路
Tarjan 複習小結
一 割點。void tarjan r i,r rt else low i min low i dfn to k if i rt sum 1 ans i 1 注意 在不聯通圖中,應當 for r i 1 i n i if dfn i tarjan i,i 這樣才能保證全部求到,注意根節點.二 橋。vo...
tarjan複習筆記
tarjan求強連通分量 割點總體思想 遍歷每乙個結點並使用並查集記錄父子關係。tarjan 是一種dfs的思想。我們需要從根結點去遍歷這棵樹。當遍歷到某乙個結點 稱之為 x xx 時,你有以下幾點需要做的。將當前結點標記為已經訪問。遞迴遍歷所有它的子節點 稱之為 y yy 並在遞迴執行完後用並查集...
Tarjan專題總結複習
dfn x x 第一次被訪問的時間順序 時間戳 搜尋樹 每個節點只訪問一次,所有訪問過的邊 x,y 構成一棵搜尋樹 low x 定義為以下節點的時間戳的最小值 1.subtree x 中的節點。2.通過 1 條不在搜尋樹上的邊,能夠到達 subtree x 的節點。void tarjan int x...