每日一题 蓝桥 发现环(Tarjan)

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[当前节点] 当上节点度dfn大于等于下节点子树最小度low时,
说明上方有节点与下方连在了一起,成环了.

#include 
#include 
#include 
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;
}

你可能感兴趣的:(每日一题 蓝桥 发现环(Tarjan))