tarjan强连通--zhengjun
强连通就是在一个有向图中任何一个点都可以到达除这个点之外的所有点。
然后,在处理的时候,就可以把这一个子图直接变成一个点。
比如说这张图
就可以缩成这张图
原图中的
就是一个强连通
也就是说只要有环,就一定有一个强连通(其实自己一个也算一个强连通)
然后,就是找环的环节~~~~重点
找环,其实就是看看可不可以搜到之前被搜到的点。可是,如果有一个大环套了一个小环,而我们却先搜到了这个小环,那么搜完了之后还是要继续搜下面的点,也就是说,我们一定要把这个点之后的点都搜完了之后才可以下结论。
所以我们可以用\(low_i\)表示\(i\)这个点最多可以上到之前搜到过的哪一个点,这样,如果\(low_i\)不是他自己,那么这里一定有一个环——强连通分量。
那么,要求出\(low_i\),就是最多能上到之前搜到过的哪一个点,就应该记录搜到这个点的顺序,存在\(dfn\)中,然后,就可以直接取\(\min\)就好了。
另外,如果现在找到了一个点的\(dfn_i\ne low_i\),那么它刚才搜了一圈的点都可以变成一个强连通,所以,就可以用一个队列来存储刚刚搜过的点,直接把队列中的数知道这个店取出来变成一个强连通就可以了。
代码
int min(int x,int y){return x<y?x:y;}
int n,m;
struct zj{
int to,nex;
}e[maxm];
int k,head[maxn];
void add(int x,int y){
e[++k]=(zj){y,head[x]};head[x]=k;
}
int dfn[maxn],low[maxn],stack[maxn],top,scc[maxn],scct,cnt;
void tarjan(int x){
dfn[x]=low[x]=++cnt;//开始时一个点对多能达到的点就是他本身
stack[top++]=x;//进入队列
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;
if(!dfn[v]){//这个点还没有搜过
tarjan(v);//继续搜这个点
low[x]=min(low[x],low[v]);//取最小值
}
else if(!scc[v])low[x]=min(low[x],low[v]);//注意要下一个点没有成为一个强连通才可以
}
if(dfn[x]==low[x]){//有一个强连通
scct++;//强连通的序号
while(stack[top-1]!=x){//出队
scc[stack[top-1]]=scct;
top--;
}
scc[stack[top-1]]=scct;//剩下最后一个他自己
top--;
}
}