强连通(strongly connected): 在一个有向图G里,设两个点 a b 发现,由a有一条路可以走到b,由b又有一条路可以走到a,我们就叫这两个顶点(a,b)强连通。
强连通图: 如果 在一个有向图G中,每两个点都强连通,我们就叫这个图,强连通图。
强连通分量strongly connected components):在一个有向图G中,有一个子图,这个子图每2个点都满足强连通,我们就叫这个子图叫做 强连通分量 。
有向图的缩点就是把有向图中强连通分量缩成一个点(道理很简单,我到了这个强连通分量的任何一点,那么这个强连通分量就能被我访问了),在处理有向图的连通性问题时有很多作用。
比如如下问题:
给出一个 0 ≤ N ≤ 105 点数、0 ≤ M ≤ 105 边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
第一行为两个整数 1 ≤ n, m ≤ 105,
接下来 M 行,每行两个整数 1 ≤ u, v ≤ 105 表示从点 u 至点 v 有一条有向边。
数据保证没有重边、自环。
第一行输出一个整数 z,表示作为答案的点集的大小;
第二行输出 z 个整数,升序排序,表示作为答案的点集。
7 10 4 5 5 1 2 5 6 5 7 2 4 2 1 2 5 3 3 5 3 6
2 4 7
不明白缩点的话,是很难做出来的,也有其他方法,这里这是举个例子来解决问题。
缩点其实难在求有向图的强连通分量上面,这个得先去了解一下点击打开链接
我们把输出的代码改一下,记录到一个数组里面并且同时给每一个点打上标记,即这些点都是一个强连通分量里面的
num记录的是强连通分量数量,p是记录第几个强连通分量里面的点,unicom表示每一个点属于第几个强连通分量。
算法复杂度O(N+E)
这样我们的预备工作就做好了。把N个点划分成了num个强连通分量块了。
问了验证有向图的联通性,我们还需要利用边的信息。
假如存a->b的边而且unicom[a]!=unicom[b]即它们俩个点不属于同一强连通分量。那么也就是说我们到达了点a,那么包括点b的强连通分量就全部可以到达了。
我们给强连通分量unicom[b]打上标记,这块不用考虑了,考虑a就行了。
算法复杂度O(E)
总时间复杂度O(E+N)
基本上这个连通性的问题就解决了,对于那些没有打上标记的强连通分量就需要选取到达了。
代码如下:
#include
using namespace std;
const int maxn=1e5+100;
vector G[maxn],p[maxn],ans;
int low[maxn],dfn[maxn],Stack[maxn],S[maxn],E[maxn],n,e,cnt,Index,num;
int link[maxn],unicom[maxn];
bool vis[maxn];
void tarjan(int u)
{
low[u]=dfn[u]=++cnt;
vis[u]=true;
Stack[++Index]=u;
for (int i=0;i