强连通分量就是有向图的极大子图
通过Tarjan算法把一个图分成多个强连通分量形成DAG,从而会具有一些很好的性质
Tarjan本质上还是递归操作,在递归的时候同时维护两个时间戳数组
其中dfn数组维护的是第一次遍历到的序号:有vis数组的作用,还有判断此点是不是这个点属于的连通分量中的根节点的作用
另外一个数组就是low数组,low[u]表示从u的子树中的结点出发,走一条交叉后向边或者边可以到达的v的dfs的最小值,并且要求v还能到达u(等价于v在栈中)
我们刚开始选到一个点u,先将其压入栈中,并且将其dfs和low中的值都赋为当前维护的计数值(每次先++),然后就开始深搜,若是深搜到的点还没有被遍历过(也就是dfn数组为0),那么就顺着这点搜下去,然后递归来更新u的low值。若是这个点更新过,而且被标记在栈中,那么直接用这个点的dfn值来更新u的low值
在遍历的时候,若是做完这个点的dfs操作后发现dfn数组和low数组中的值相同,那么就将其当做一个连通分量中的根节点并开始用栈往外弹在同一个连通分量中的所有的点(要用一个数组模拟栈,就因为从根节点开始往栈中压元素,根节点自然是在栈底的,那么根节点以及在根节点上方的元素自然同属于一个连通分量)
所以我们还需要一个数组来装同一个连通分量中的所有元素,同时还需要用到一个do-while循环来弹出同一个连通分量中的所有元素
tarjan模板呈上
void tarjan(int u)
{
dfn[u] = low[u] = ++timestamp;
stk[++top] = u,in_stk[u] = 1;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(!dfn[j])
{
tarjan(j);
low[u] = min(low[u],low[j]);
}
else if(in_stk[j]) low[u] = min(low[u],dfn[j]);
}
if(dfn[u]==low[u]) //往栈里存元素+出栈
{
++scc_cnt;
int y;
do
{
y = stk[top--]; //出栈
in_stk[y] = 0; //标记点
id[y] = scc_cnt;
s[scc_cnt]++; //记录栈中元素个数
}while(y!=u);
}
}
要加油啊~