You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
python/TangDou/AcWing/CheckCircle/HDU3342【强连通分量判环】.cpp

70 lines
3.3 KiB

2 years ago
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 10, M = N << 1;
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfn[N], low[N]; // u节点被访问的时间点/ u通过有向边可回溯到的最早的时间点
int stk[N], top; // 栈数组stk,栈顶指针top
bool in_stk[N]; // 是不是在栈中
int timestamp; // 时间戳
int scc; // 连通块个数
void init_tarjan(int n) { // 封装的tarjan算法初始化函数
//初始化
timestamp = scc = top = 0;
memset(in_stk, false, sizeof(in_stk));
memset(dfn, 0, sizeof(dfn));
}
void tarjan(int u) {
dfn[u] = low[u] = ++timestamp; //分配被访问的时间点dfn[u] 和 u通过有向边可回溯到最早的时间点
stk[++top] = u; // u节点入栈
in_stk[u] = true; //记录u在栈中
for (int i = h[u]; ~i; i = ne[i]) { //遍历u的所有出边
int j = e[i];
if (!dfn[j]) { //如果j没有被访问过
tarjan(j); //深度搜索,递归访问它
//等孩子整完事,用一下孩子的结果 尝试更新自己
low[u] = min(low[u], low[j]); //类似后序遍历u通过有向边可回溯到的最早时间可能更新为它儿子可以获得的最早时间点
} else if (in_stk[j]) //被访问过但不在栈中比如视频2中提到的e,已经出栈是独立的强连通分量了就不需要处理只需要处理b
low[u] = min(low[u], low[j]); //在栈中可以利用j更新u的回溯值,j是u的祖先
}
//如视频所讲发现u的被访问时间点dfn[u]和可回溯最早时间点low[u]一致,说明它不能回溯到更早,需要把栈顶到它之间所有点出栈
if (dfn[u] == low[u]) { // u节点就是进入强连通分量的入口点
++scc; // 连通块个数++,如视频 d c b组成了一个强连通分量
int x; // 从栈顶到b的所有点组成了一个强连通分量
do {
x = stk[top--];
in_stk[x] = false;
} while (x != u);
}
//能有机会从栈中批量弹出的,是一组能构成强连通分量的节点。而,有的节点因为不与其它节点构建强连通分量,就永远无法从栈中弹出
//如视频所说a就是永远在栈中它是一个孤立的强连通分量。
//如视频复杂的例子所说e点是独立的强连通分量它满足dfn[u]=low[u],它是独立的强连通分量,scc++,同时 x=stk[top--]
}
// 调用示例
// for (int i = 0; i < n; i++)
// if (!dfn[i]) tarjan(i);
int main() {
int n, m;
while (~scanf("%d%d", &n, &m) && n) {
idx = 0;
memset(h, -1, sizeof(h));
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
//初始化
init_tarjan(n);
for (int i = 0; i < n; i++)
if (!dfn[i]) tarjan(i);
//强连通分量数量等于节点数,说明无环,并且是一个连通图
scc == n ? puts("YES") : puts("NO");
}
return 0;
}