竞赛中找环有许多种问题,判断是否有环,找到环上的点,找到环上的边等等。
而只需要找到环上相邻的两点,或者环上的一条边就可以解决这三个问题。
有向图中,可以用拓扑排序的方法,把将拓扑排序完后限制条件仍未被清零的点即在环上的点。
#include <bits/stdc++.h>
#define N 1000101
using namespace std;
int n, m, cnt, lin[N], deg[N];
struct edg {
int to, nex;
}e[N];
inline void add(int f, int t)
{
deg[t]++;
e[++cnt].nex = lin[f];
e[cnt].to = t;
lin[f] = cnt;
}
void topu()
{
queue <int> q;
for (int i = 1; i <= n; i++)
if (!deg[i]) q.push(i);
while (!q.empty())
{
int cur = q.front(); q.pop();
for (int i = lin[now]; i; i = e[i].nex)
{
int to = e[i].to;
deg[to]--;
if (!deg[to]) q.push(to);
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
topu();
for (int i = 1; i <= n; i++)
if (deg[i])
printf("%d ", i);
return 0;
}
无向图中,就不能用拓扑排序了,比较好的方法就是并查集。
并查集通过判断每一条边的两个端点是否在一个一个集合内来找到在同一个环上的两个边,然后以这两个点为起点和终点搜索,最终输出所有在他们路径上的点。
#include <bits/stdc++.h>
#define N 1001011
using namespace std;
int n, m, cnt, fa[N], lin[N], vis[N], ha[N];
int find(int a)
{
if (fa[a] == a)
return a;
return fa[a] = find(fa[a]);
}
struct edg {
int to, nex, from;
}e[1001001];
inline void add(int f, int t )
{
e[++cnt].to = t;
e[cnt].from = f;
e[cnt].nex = lin[f];
lin[f] = cnt;
}
int dfs(int now, int end)
{
if (now == end) return 1;
int flag = 0;
for (int i = lin[now]; i; i = e[i].nex)
{
int to = e[i].to;
if (vis[to]) continue;
vis[to] = 1;
flag = max(flag, dfs(to, end));
if (flag) ha[now] = ha[to] = 1;
vis[to] = 0;
}
return flag;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 1, a, b; i <= n; i++)
{
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
int faa = find(a), fab = find(b);
memset(vis, 0, sizeof(vis));
if (faa == fab) //说明他们已经在同一个集合里
dfs(a, b);
fa[faa] = fab;
}
for (int i = 1; i <= n; i++)
if (ha[i])
printf("%d ", i);
return 0;
}