什么是强联通分量
在一个有向图中:
1.如果两个点可以通过有向边互相联通,那么称这两个点强联通;
2.如果图中任意两个点都强联通,那么称这个有向图为强连通图(一个点也是强连通图);
3.一个非强联通图中的极大强联通子图(不被另一个更大的强联通子图包含),称为这个图的强联通分量。
如图:
图中的强连通分量有:{1,2,4}和{3}。
从中可以看出强连通分量有几个明显特征:
1.各个强连通分量中不会有重复的点。
2.一个强连通分量中,从任意一个点u都能通过有向边经过所有点,最后回到点u。
什么是tarjan
从上面的结论中可知,强连通分量就是极大有向环。
所谓tarjan,肯定是和DFS相关(不要问我为什么)。假设从u点DFS起,如果u在一个强连通分量内,那么最后肯定会回到u点。
tarjan算法的具体实现就是:
1.用dfn[u]记录u的搜索编号(表示u是第几个被搜到的点);
2.用low[u]表示从u往下搜可以搜到的搜索编号最小的点,初始化low[u]为dfn[u];
3.把u放进栈中,站内的点表示还未归为强连通分量内的点;
4.枚举被u连接的点往下搜,设这个点为v,如果v在栈中,即v已被搜过且未归为强连通分量内,则low[u]=min(low[u],dfn[v]);如果v没被搜过,则v肯定未归为强连通分量内,那么就DFS[v],并记录low[u]=min(low[u],low[v]);
5.最后,如果dfn[u]等于low[u]了,说明从u往下搜又会回到u(或者从u无法往下搜),由于在栈内,从u往下搜到的点都会堆在u上面,所以把栈内堆在u上的点和u一起出栈,并归为一个强连通分量。
在所有u搜到的点中,无法通过有向边回到u的点(无法与u强联通),要么连接到了一个dfn值比u大的点v,形成了一个小环,然后被出栈;
要么没有形成环,low值保持原来的与dfn值相等,也会被出栈(这就是一个点的强联通分量),
所以栈内剩下的堆在u上的点都是可以与u强联通的点。
比如上面那个图:
从点1搜起:
从2往下搜到4:
4连接到1,发现1在栈内,low改变,并回溯到2:
接下来,从2往3搜:
3无法再搜,dfn=low,出栈:
最后回到1,发现dfn=low,1、2、4一并出栈:
求出2个强连通分量:{3},{1,2,4}。
tarjan核心代码
void tarjan(int x){
dfn[x]=low[x]=IN++;
in_s[x]=1;
S.push(x);
for(int i=0;i<G[x].size();i++){
if(!dfn[G[x][i]]){
tarjan(G[x][i]);
low[x]=min(low[x],low[G[x][i]]);
}
else if(in_s[G[x][i]])low[x]=min(low[x],dfn[G[x][i]]);
}
if(dfn[x]==low[x]){
num++;int v;
do{
v=S.top();
S.pop();
belong[v]=num;
M[num]=min(M[num],mn[v]);
in_s[v]=0;
}while(v!=x);
}
}