推荐博客 : https://www.cnblogs.com/wozaixuexi/p/8321602.html
https://blog.csdn.net/qq_34374664/article/details/77488976
在学习这个算法前,要先知道 : 连通图,强连通图,强连通分量
连通图 : 如果两个顶点可以相互到达,则称这两个点强连通
强连通图 : 如果有向图中每两个顶点都连通,则称此图是一个强连通图
强连通分量 : 在一个有向图中,有一个子图,这个子图中的没两个点都满足强连通,我们就把这个子图叫做强连通分量。
处理强连通问题有两种算法
1 、 tarjin 算法
这里面有两个关键的数组 , dfn[ ] 数组,意思是在dfs过程中,当前的这个节点是第几个被遍历到的点,其实就是个时间戳
low[ ] 数组,每个点所在的这棵树种,最小子树的根
当 dfn[ u] = low[ u ] 表示 u 或 u 的子树构成一个强连通分量。
我们求强连通分量,最终我们想要得到的就是一个 DAG 图,在 Tarjin 中缩点,构成一个无环的有向图
例题 :
输入: 一个图有向图。
输出: 它每个强连通分量。
input:
6 8
1 3
1 2
2 4
3 4
3 5
4 6
4 1
5 6
output:
6
5
3 4 2 1
代码示例 :
const int maxn = 1e5+10; vector<int>ve[maxn]; vector<int>id[maxn]; // 强连通的编号 int n, m; int dfn[maxn], low[maxn]; // 每个点在这棵树中,最小的子树的根 int tot = 0, key = 0; int Stack[maxn], belong[maxn]; // 缩点 bool instack[maxn]; int scc; // 强连通分量的个数 void tarjin(int x){ low[x] = dfn[x] = ++key; // 注意是 ++在前,因为下面下面深搜的判断是为0表示没访问过的点,才去搜 Stack[tot++] = x; instack[x] = true; for(int i = 0; i < ve[x].size(); i++){ int to = ve[x][i]; if (!dfn[to]) { tarjin(to); low[x] = min(low[x], low[to]); } else if (instack[to]){ low[x] = min(low[x], dfn[to]); } } if (low[x] == dfn[x]){ scc++; int v; do{ v = Stack[--tot]; instack[v] = false; belong[v] = scc; id[scc].push_back(v); printf("%d ", v); } while(v != x); printf(" "); } } int u[maxn], v[maxn], in[maxn]; vector<int>ans; int main() { //freopen("in.txt", "r", stdin); //freopen("out.txt", "w", stdout); cin >> n >> m; int a, b; memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(instack, false, sizeof(instack)); memset(in, 0, sizeof(in)); for(int i = 1; i <= m; i++){ scanf("%d%d", &u[i], &v[i]); ve[u[i]].push_back(v[i]); } for(int i = 1; i <= n; i++){ if (!dfn[i]) tarjin(i); } //for(int i = 1; i <= n; i++) printf("%d ", belong[i]); return 0; } /* 6 8 1 3 1 2 2 4 3 4 3 5 4 6 4 1 5 6 */