版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82977344
一.概述.
强连通分量SCC是基于有向图的一个概念,即“极大连通分量”.有向图的强连通分量就是说一张图G的子图G',G'的每一个点u都可以遍历到这张图上的任意一个点v,且这张子图G'极大,极大的意思可以参考双连通分量的极大.
二.强连通分量与tarjan算法.
tarjan算法可以在线性时间内找出一张图的所有强连通分量.
与无向图的tarjan算法类似,有向图tarjan算法也需要引入搜索树的概念,也有树边与非树边还有low值与dfn值的概念.这些概念在无向图部分已经讲解,这里不再赘述.
但是在有向图时,有可能会出现一条非树边连接的两个节点不是祖先与后代的关系.
当一个点x满足low[x]=dfn[x]时,我们发现这个点的子树中满足low[y]=dfn[x]的点y,所有点y就会与x组成一个强连通分量.
那么我们可以维护一个栈,当一个点x被遍历到时,将点x压入栈.遍历完整棵子树后,若dfn[x]=low[x],则我们点x到栈顶的所有点都标记为一个新的强连通分量,并将点x到栈顶的所有点出栈.
正如上面所说,会出现非树边连接的两个节点不是祖先与后代的关系,所以我们将low值定义为,若dfn[y]=low[x],则y必须在栈中.
tarjan算法求解有向图强连通分量代码如下:
扫描二维码关注公众号,回复:
3619008 查看本文章
void tarjan(int k){
dfn[k]=low[k]=++num;
st[++ts]=k;use[k]=1;
for (int i=lin[k];i;i=e[i].next){
int y=e[i].y;
if (!dfn[y]){
tarjan(y);
low[k]=min(low[k],low[y]);
}else if (use[y]) low[k]=min(low[k],low[y]);
}
if (dfn[k]^low[k]) return;
cnt++;
while (st[ts]^k){
scc[st[ts]]=cnt;
use[st[ts--]]=0;
}
scc[k]=cnt;use[st[ts--]]=0;
}
void contract(){
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i);
}
三.SCC缩点.
我们可以发现,当我们将每一个SCC都看成一个点之后,整张图就会变成一张有向无环图,所以我们有了SCC缩点.
SCC缩点就是新建一张图,将所有SCC都缩成一个点之后的一张DAG.
代码如下:
void tarjan(int k){
dfn[k]=low[k]=++num;
st[++ts]=k;use[k]=1;
for (int i=lin[k];i;i=e[i].next){
int y=e[i].y;
if (!dfn[y]){
tarjan(y);
low[k]=min(low[k],low[y]);
}else if (use[y]) low[k]=min(low[k],low[y]);
}
if (dfn[k]^low[k]) return;
cnt++;
while (st[ts]^k){
scc[st[ts]]=cnt;
use[st[ts--]]=0;
}
scc[k]=cnt;use[st[ts--]]=0;
}
void contract(){
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i);
for (int i=1;i<=n;i++)
for (int j=lin[i];j;j=e[j].next)
if (scc[i]^scc[e[j].y])
insc(scc[i],scc[e[j].y]);
}