【51nod 2846】无向图找无弦环(tarjan)
无向图找无弦环
题目链接:51nod 2846
题目大意
给你一个无向图,要你找一个无弦环。
无弦环是指一个环里面的点除了环连着的点对别的之间都没有边。
思路
首先由一个很暴力的想法就是我们找最小环,而这个肯定是可以的。
因为我们会发现如果一个环有弦是因为里面插了一条(或者若干条)边,那我们完全可以把它分成两个(或者多个)小的环,那我们找到最小的环,它里面肯定不就没有边了。
但是你会发现常规的找最小环的方法是 Floyed 算法,复杂度达到了 \(O(n^3)\) 过不了,而且你要输出方案是会比较麻烦的。
那我们考虑别的方法,考虑有什么别的东西也可以找环。
不难想到 Tarjan 这个东西,而且你想一想会发现你可以把它简化一下得到。
那每次到一个点我们先看它的每个儿子中有没有可以到之前到过的点,那如果有就代表出现环了,那我们要保证没有弦,那我们就可以找 \(dfs\) 序距离当前最近的一个点,然后把栈中的那个部分拿出来输出即可。
如果没有,那我们就走,走就随便走,毕竟你找到一个答案就可以直接结束整个程序了。
然后要记得你搜完一个个要把它从栈中弹出来。
然后搞就可以啦。
代码
#include<cstdio>
#include<cstdlib>
using namespace std;
const int N = 2e5 + 100;
struct node {
int to, nxt, op;
}e[N << 1];
int n, m, le[N], KK;
int dfn[N], sta[N];
int ans[N];
void add(int x, int y) {
e[++KK] = (node){y, le[x], KK + 1}; le[x] = KK;
e[++KK] = (node){x, le[y], KK - 1}; le[y] = KK;
}
void tarjan(int now, int bef) {
dfn[now] = ++dfn[0]; sta[++sta[0]] = now;
bool end = 0; int pla = -1;
for (int i = le[now]; i; i = e[i].nxt) {
if (e[i].op == bef) continue;
if (!dfn[e[i].to]) continue;
else {
if (!end) pla = e[i].to, end = 1;
else pla = dfn[pla] < dfn[e[i].to] ? e[i].to : pla;
}
}
if (end) {
while (sta[sta[0]] != pla) ans[++ans[0]] = sta[sta[0]--];
ans[++ans[0]] = sta[sta[0]];
printf("%d\n", ans[0]); for (int i = 1; i <= ans[0]; i++) printf("%d ", ans[i]);
exit(0);
}
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].op == bef) continue;
else tarjan(e[i].to, i);
sta[sta[0]--] = 0;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y; scanf("%d %d", &x, &y);
add(x, y);
}
for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i, 0);
return 0;
}