强连通分量
一张有向图,如果该图任意两点之间连通,则称该图强连通。
有向图的子图中的任意点相互连通叫做强连通子图,而如果该子图不能再大了也就是已达到最大,那么该子图叫做极大强连通子图,也就是强连通分量。
1
2
3
4
5
6
7
那么该图有三个强连通分量,分别是{1,2,3}、{4,5,6}、{7}。
实现
cpp
void dfs(int x){
dfn[x]=low[x]=++cnt;//dfn序
s.push(x);//推入栈
for(int i=0;i<E[x].size();i++){
int v=E[x][i];
if(dfn[v]==0){//如果没来过
dfs(v);
low[x]=min(low[x],low[v]);//更新
}else if(gp[v]==0){//不然看他有没有被分过组
low[x]=min(low[x],dfn[v]);//更新
}
}
if(low[x]==dfn[x]){//如果他没被其他点更新过
gp[x]=++id;//分组
while(s.top()!=x){//如果栈顶不是x
gp[s.top()]=id;
s.pop();
}
s.pop();
}
}
缩点
就是找到强连通分量后将整个强连通分量缩成一个点,按上图作样例。
1* 1,2,3
2* 4,5,6
3* 7
打星号的是强连通分量的编号。
实现
cpp
for(int i=1;i<=n;i++){
for(int j=0;j<E[i].size();j++){
int v=E[i][j];
if(gp[i]!=gp[v]){//如果两点属于的组不一样
e[gp[i]].push_back(gp[v]);//新图连边
}
}
}
边双连通分量
把强连通分量中的有向边替换成无向边,就是一个边双连通分量,所以求边双连通分量更求强连通分量差不多,只需多判一个重边就行了。
cpp
void dfs(int x,int fa){
dfn[x]=low[x]=++cnt;
s.push(x);
bool cc=0;
for(int i=0;i<E[x].size();i++){
int v=E[x][i];
if(v==fa){//发现跑了回来
if(!cc){//如果还没出现过
cc=1;
}else{
low[x]=min(low[x],dfn[v]);//否则更新
}
continue;
}
if(dfn[v]==0){
dfs(v,x);
low[x]=min(low[x],low[v]);
}else if(gp[v]==0){
low[x]=min(low[x],dfn[v]);
}
}
if(low[x]==dfn[x]){
gp[x]=++id;
while(s.top()!=x){
gp[s.top()]=id;
s.pop();
}
s.pop();
}
}