强连通原理
用途:缩点&最小点基问题
原图经强连通缩点后变成了一个有向无环图。有向无环图变成强连通图只需增加max(g,h)条有向边即可(G为出度为0的节点数,H为入度为0的节点数,特别地当缩点后的图只有一个点时,答案为0
数组定义:
dfn[v]为遍历到点v的时间
low[v]开始遍历点v时初始化为dfn[v],遍历完点v时则保存所有与邻接点u的low[u]最小值
伪代码:
tarjan(u){//为节点u设定次序编号和Low初值
DFN[u]=Low[u]=++Index
Stack.push(u) // 将节点u压入栈中
for each (u, v) in E // 枚举每一条边
if (v is not visted) //如节点v未被访问
tarjan(v) // 继续向下找
Low[u] = min(Low[u], Low[v])
else if (v in S) // 如果节点u还在栈内
Low[u] = min(Low[u], DFN[v])
if (DFN[u] == Low[u]) //若节点u是强连通分量的根
repeat v = S.pop //将v退栈
print v //v为该强连通分量中一个顶点
until (u== v)
}
算法模拟:如下图
从1进入 DFN[1]=LOW[1]=++index-1
入栈 1
由1进入2 DFN[2]=LOW[2]=++index-2
入栈 1 2
由2进入3 DFN[3]=LOW[3=++index-3
入栈 1 2 3
由3进入 6 DFN[6]=LOW[6]=++inde-4
入栈 1 2 3 6
6无出度,之后判断 DFN[6]==LOW[6]
说明6是个强连通分量的根节点:
6及6以后的点出栈。
栈: 1 2 3
退回节点3
Low[3] = min(Low[3], Low[6]) LOW[3]还是 3
节点3 也没有再能延伸的边了
判断 DFN[3]==LOW[3]
说明3是个强连通分量的根节点:
3及3以后的点 出栈。
栈: 1 2
之后退回 节点2 嗯?!往下到节点5
DFN[5]=LOW[5]=++index-5
入栈 1 2 5
结点5 往下找,发现节点6 DFN[6]有值,被访问过及不在栈中。就不管它。
继续 5往下找,找到了节点1 他爸爸的爸爸。。DFN[1]被访问过并且还在栈中,说明1还在这个强连通分量中,值得发现
Low[5] = min(Low[5], DFN[1])
确定关系,在这棵强连通分量树中,5节点要比1节点出现的晚,故5是1子节点
LOW[5]= 1
由5继续回到2
Low[2] = min(Low[2], Low[5])LOW[2]=1;
由2继续回到1 判断
Low[1]=min(Low[1],Low[2]) LOW[1]还是 1
1还有边未走。发现节点4,访问节点4
DFN[4]=LOW[4]=++index-6
入栈 1 2 5 4
由节点4,走到5,发现5被访问过,5在栈里
Low[4] = min(Low[4], DFN[5]) LOW[4]=5
说明4是5的一个子节点。
由4回到1.
回到1,判断
Low[1]=min(Low[1], Low[4]) LOW[1]还是1
判断 LOW[1] == DFN[1]
相等说明以1为根节点的强连通分量已找完,将栈中1及之后进栈的所有点出栈