定义:在一个有向图中,选取一个点集S, 如果对于S中任意两点u,v 都满足u 可以到达v ,则称S是强连通的。
如果一个强连通点集S中,不能加入更多的点使得它仍是强连通,则称S是强连通分量
(我的理解强连通分量是有向图的子集,属于它的任意两点可互通/成环)
两种算法:1 tarjan: O(n+m), 细节较多
2 Kosaraju(n+m), 细节较少,可继续优化
Kosaraju
有两次深搜,第一次用反向图深搜得到每个点的出栈序列,第二次按照q[n]...q[1]的顺序去dfs
const int MAXN = 100 ; //最大点数
bool vis[MAXN];
int f[MAXN],q[MAXN],t;
int G[MAXN*MAXN],NXT[MAXN*MAXN],V[MAXN*MAXN];
int G_back[MAXN*MAXN],NXT_back[MAXN*MAXN],V_back[MAXN*MAXN];
void dfs1(int x)
{
vis[x]=1;
for(int i=G_back[x];i;i=NXT_back[i]) if(!vis[V_back[i]]) dfs1(V_back[i]);
q[++t]=x;
}
void dfs2(int x,int y)
{
vis[x]=0, f[x]=y;
for(int i=G[x];i;i=NXT[i]) if(vis[V[i]]) dfs2(V[i],y);
}
int main()
{
int i,n;
for(t=0,i=1;i<=n;i++) if(!vis[i]) dfs1(i);
for(i=n;i;i--) if(vis[q[i]]) dfs2(q[i],q[i]);
}
Tarjan
dfn时间戳,low记录所属强连通分量,先初始化这两个值
遍历能到达的点,没去过的继续深搜,搜完后更新low。如果去过,看是否在栈内,在的更新low
遍历完后看dfn和low是否相等,相等则再开一个新的强连通分量,清除栈里的属于这个强连通分量的值。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100;
int co[MAXN]; //记录,所属强连通分量编号
int dfn[MAXN],low[MAXN]; //记录搜索顺序,记录所属强连通
int st[MAXN];
int num,col,top;
void Tarjan(int u)
{
dfn[u]=low[u]=++num;
st[++top]=u;
for(int i=fir[u];i;i=nex[i])
{
int v=to[i];
if(!dfn[v])
{
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!co[v]) //有的版本用isinstack数组记录是否在栈内,用isinstack[v]做判断条件,这里等价
{
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u])
{
co[u]=++col;
while(st[top]!=u)
{
co[st[top]]=col;
--top;
}
--top;
}
}