标签:例题 问题 ++ 代码 mem for FN 就是 两种
推荐博客 : 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("\n");
}
}
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
*/
标签:例题 问题 ++ 代码 mem for FN 就是 两种
原文地址:https://www.cnblogs.com/ccut-ry/p/8902316.html