有向图的强连通分量
对于一个有向图,连通分量:对于分量中任意两点u,v,必然可以从u走到v,且从v走到u.
强连通分量:极大连通分量。
求出强连通分量后,可以通过将强连通分量缩点的方式,将有向图转化成有向无环图。
求强连通分量的方法:tarjan O(n+m),时间复杂度是线性的
1 . 采用dfs来遍历整个图,可以将边分为四类 (x->y)
- 树枝边 x是y的父节点
- 前向边 x是y的祖先,x可以到达y
- 后向边 y是x的祖先, x可以到达y
- 横叉边 x可以到达已经遍历且已经回溯过的y点
2 .如何确定点在连通分量中
- 存在一条后向边,使其指向祖先节点
- 存在一条横插边,走过了横叉边,又存在一条后向边指向原来的点的祖先节点
3 .引入一个时间戳的概念
- dfn[u] 表示遍历到u的时间,也就是dfs序
- low[u] 表示从u所在强连通分量中,所能遍历到的层次最高的点的dfs序。
- 当dfs[u]==low[u]时,表示u是其所在的强连通分量的最高点
4 . 对于tarjan算法,我的理解是每次都是对以当前的点为根节点的子图进行操作的,遍历完子图中的所有点时,如果dfs[u]==low[u]表示当前的根就是在强连通图中,一个一个清掉栈中的元素,在没有清掉当前点的时候,清理出的元素都是强连通图中的元素。
int n,m;
int h[N],ne[M],e[N],idx=0;
int dfn[N],low[N],tm=0;
int vis[N];
int id[N],cnt=0,ids[N];
int dout[N];
stack<int> s;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++tm;
s.push(u);
vis[u]=1;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(!dfn[j])
{
tarjan(j);
low[u]=min(low[u],low[j]);
}
else if(vis[j]) low[u]=min(low[u],dfn[j]);
}
if(dfn[u]==low[u])
{
++cnt;
int y;
do{
y=s.top();
s.pop();
vis[y]=0;
id[y]=cnt;
ids[cnt]++;
}while(y!=u);
}
}
总结:使用完tarjan算法后,就已经将有向图转化为有向无环图(DAG)。
而对于有向无环图,有以下几种应用:(注意以下的点是经过缩点之后的,点代表的是一个强连通分量,其中的点的个数为非零个)
1 是否存在点能被其他所有点到达。
只需计算出度为零的点有几个:
- 有一个的话,表示存在,个数为当前连通快的大小。
- 有多个的话,则为零个
2 选取最少的点数,即可遍历整个图文章来源:https://www.toymoban.com/news/detail-497362.html
- 找出入度为零的强连通块数,即为答案。(强连通块中的点互相可以到达)
3 需要添加最少的边数,使整个图变为强连通分量文章来源地址https://www.toymoban.com/news/detail-497362.html
- 当强连通块的数量为1时,不需要加边
- 不为1时,答案为在入度为零的点个数和出度为零的点个数取最大值,因为最优情况时,每个出度为零的点向入度为零的点加一条边,反之亦然。
到了这里,关于有向图的强连通分量的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!