定义
对于无向连通图G=(V,E),Tarjan算法可以在线性时间内求出无向图的割点和桥。
割点:对于x∈V,如果删除某个点x,图的联通分量增多(G分裂为两个或者两个以上的子图),则x为割点。
桥 :对于e∈E,如果删除e,G分裂成两个不相连的子图,则称e为桥或割边。
时间戳(T[i]):DFS的过程中,第一次访问到节点的时间,将N个节点依次标记为1-N。
搜索树:按照DFS的顺序建树。
追溯值(low[i]):low[i] = min(T[i通过不在搜索树上的边到达的点] , i的子树中的节点)。
割边判定法则:当节点x的子节点y满足T[x] < low[y] 时,边(x,y)为桥。 证明:当T[x] < T[y] 时 y走除了(x,y)外的任意边都无法到达时间戳更小的节点。
桥一定是搜索树中的边,简单环中的边一定都不是桥。
模板(完美支持重边 powered by 李煜东)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int dfn[maxn],low[maxn],n,num,tot,m;
int head[maxn],ver[maxn<<1],Next[maxn<<1];
bool bridge[maxn<<1];
void add(int x , int y)
{
ver[++tot] = y,Next[tot]=head[x],head[x] = tot;
}
void Tarjan(int x, int in_edge)
{
dfn[x] = low[x] = ++num;
for(int i = head[x] ; i ; i = Next[i])
{
int y = ver[i];
if(!dfn[y])
{
Tarjan(y,i);
low[x] = min(low[x],low[y]);
if(low[y] > dfn[x])
{
bridge[i] = bridge[i^1] = true;
}
}
else if(i != (in_edge^1))
{
low[x] = min(low[x] , dfn[y]);
}
}
}
int main()
{
cin >> n >> m;
// num = 0;
tot = 1;
for(int i = 0 ; i < m ; i++)
{
int x,y;
cin >> x >> y;
add(x,y);add(y,x);
}
for(int i = 1 ; i <= n ; i++)
{
if(!dfn[i])
Tarjan(i,0);
}
for(int i = 2 ; i < tot ; i += 2 )
{
if(bridge[i])printf("%d %d\n",ver[i^1],ver[i]);
}
}