【演算法模板】tarjan
阿新 • • 發佈:2022-04-06
Tarjan演算法基於深度優先搜尋, 每個強連通分量為搜尋樹中的一棵子樹. 搜尋時, 把當前搜尋樹中未處理的結點加入堆疊, 回溯時就可判斷棧頂到棧中的結點是否為一個強連通分量.
dfn[u]: 記錄結點u在DFS過程中被遍歷到的次序號(時間戳).
對於一棵DFS樹, 對於原圖中的非樹邊, 為便於描述, 定義:
前向邊: 祖先->兒子的邊.
後向邊: 兒子->祖先的邊.
橫叉邊: 沒有祖先兒子關係的邊(注意橫叉邊
只會往dfn減小的方向連線).
low[u]: 記錄結點u或u的子樹能夠追溯到的dfn最小的值(棧中標號的最小點)
由定義, 顯然有:
⚫ 若(u,v)為樹邊, low[u] = min{dfn[u], low[v]};
⚫ 若(u,v)是指向棧中結點的後向邊, low[u] = dfn(v).
定理: 當dfn[u] = low[u]時, 以u為根的搜尋子樹上所有結點構成一個強連通分量.
證明: dfn[u]表示u點被DFS到的時間, low[u]表示u和u所有的子樹所能到達的點中dfn最小值.
dfn[u]=low[u], 這說明u點及u的子樹結點最多隻有指向u點的邊, 而沒有指向u的祖先的邊了.
顯然, 遍歷過的結點從u出發又最終回到u形成一個環, 即u點與它的子孫結點構成了強連通分量.
inline void paint(int x) { s.pop(); ins[x] = 0; color[x] = colorNum; colorSize[colorNum]++; } void tarjan(int u) { dfn[u] = low[u] = ++dfnn; s.push(u); ins[u] = vis[u] = 1; for (int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if (!dfn[v]) { //前向邊 tarjan(v); low[u] = min(low[u], low[v]); } else if (ins[v]) //後向邊(排除橫叉邊) low[u] = min(low[u], dfn[v]); } if (dfn[u] == low[u]) { colorNum++; while(s.top() != u) { int x = s.top(); paint(x); } paint(u); } }