PREV-49
发现环
问题描述
小明的实验室有N台电脑,编号1~N。原本这N台电脑之间有N-1条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
输入格式
第一行包含一个整数N。
以下N行每行两个整数a和b,表示a和b之间有一条数据链接相连。
对于30%的数据,1 <= N <= 1000
对于100%的数据, 1 <= N <= 100000, 1 <= a, b <= N
输入保证合法。
输出格式
按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。
样例输入
5
1 2
3 1
2 4
2 5
5 3
样例输出
1 2 3 5
之前做过一道割边的题,想起了这道当时不会的题.
当时学的割边题: 华华和月月逛公园
做这题时,直接套的模板,死的贼惨.
刚好蓝桥VIP也没了,那里出问题也不知道,
看别人题解,思路不一样也看不太懂.
懵逼了一晚上,一顿瞎改,终于过了.
第二天才想起这时割点.
思路:就是tarjan算法,这里稍微解释一下.(最好看其他人的Tarjan专题详解)
重要的两个数组dfn,low
dfn[i]表示 i 入栈时间,下一个点的入栈时间肯定比当前点大
low[i]表示 i 子树中最小入栈时间dfn(包括自己)
若没有环,i 子树中最小入栈时间肯定是自己,因为下节点的dfn更大
若没有环, dfn[当前节点]<low[下节点] (最重要的理解部分)
当上节点度dfn大于等于下节点子树最小度low时,
说明上方有节点与下方连在了一起,成环了.
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e5 + 5;
struct Edge {
int to;
int next;
} e[N << 1];
int tot = 0;
int head[N];
void add(int u, int v) {
e[++tot].to = v;
e[tot].next = head[u];
head[u] = tot;
}
int a[N];
bool vis[N << 1];
int dfn[N], low[N];
int cnt = 0;
void Tarjan(int u,int fa) {
dfn[u] = low[u] = ++cnt;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (v==fa) {
continue;
}
if (!dfn[v]) {
Tarjan(v,u);
low[u] = min(low[u], low[v]);
if (dfn[u] >= low[v]) {
a[u]++;
}
} else {
low[u] = min(low[u], dfn[v]);
if (dfn[u] >= low[v]) {
a[u]++;
}
}
}
}
int main() {
int n;
scanf("%d", &n);
int u, v;
for (int i = 0; i < n; i++) {
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
Tarjan(1,0);
for (int i = 1; i <= n; i++) {
if (a[i]) {
printf("%d ", i);
}
}
return 0;
}