求解强连通分量有两种算法,tarjan和kosaraju
首先来俩概念:
强连通:在有向图G中,若两个点u、v是互相可达的,则称u和v是连通的。如果G中任意两个点都是互相可达的,称G是强连通图。
强连通分量:如果一个有向图G不是强连通图,那么可以把它分成多个子图,其中每个子图的内部是强连通的,而且这些子图已经扩展到最大,不能与子图外的任意点强连通,称这样一个“极大强连通”子图是G的一个强连通分量(Strongly Connected Component)
有个常见的问题:求G中有多少个scc?
我们先来尝试着把每个scc抽象成一个个的群岛,群岛由有着很多或很少的小岛组成,群岛内部连通;群岛与其他群岛之间有单向道路连接,且不会形成环路。然后将每一个群岛虚拟成一个个点,最后得到一个有向无环图GG,GG中点的数量即为scc数量(还能推出:每个群岛挖掉后,并不会影响其他岛内部的连通性)
那么一开始想到的一定是暴力!!!(暴力不可取。。。)
暴力思路:对每个点求连通性,然后进行比较,互相连通的点就组成了scc(dfs或bfs可实现)
。。。时间复杂度:
下面介绍Kosaraju和Tarjan算法(时间复杂度:)
Kosaraju
原理:
1、将有向图G的所有边反向构建反图rG,rG不会改变原图G的强连通性。(人话:G的scc数量和rG的数量相同)
2、对G和rG各做一次dfs,可以确定scc数量
有一个我之前特别困惑的点:dfs干啥用的?
——对虚拟点进行拓扑排序,排序后将优先级最高的虚拟点进行反dfs,就可以求出被群岛与外界隔离的那些小岛了
模版在下面
Tarjan
Kosaraju是在图中一个一个将scc给挖出来,而Tarjan能在一次dfs就将所有点按照scc分开
其实理解了Kosaraju,来理解Tarjan也不是很难(个人意见)
有几个变量需要注意:
low[i]:表示能返回到的最远祖先
num[i]:表示第几次循环遍历到i(不太重要的样子)
步骤:首先dfs,赋值每个点的low,然后判断low是否与该点的num值相等,如果相等,那么这个点就是自己的祖先点。额。就没了。。(是不是挺简单的~~)
下面上题目::
B3609 [图论与代数结构 701] 强连通分量
一道模版题,下面依次提供Kosaraju和Tarjan两种做法:
Kosaraju::
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int>g[N],scc[N];
stack<int>st;
int n,m;
int f[N];
int low[N];
int num[N];
int dfn,cnt;
void dfs(int u)
{
dfn++;
low[u]=num[u]=dfn;
st.push(u);
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(!num[v])
{
dfs(v);
l

本文介绍了强连通分量的概念,探讨了如何使用暴力方法和Kosaraju、Tarjan算法高效求解有向图中的强连通分量。Kosaraju通过反向图和两次dfs确定scc数量,而Tarjan则在一次dfs中划分。文章提供了具体的C++代码示例,展示了这两种算法在不同场景的应用。
最低0.47元/天 解锁文章
226

被折叠的 条评论
为什么被折叠?



