【题解】P8655 题解
P8655 题解
思路分析
一道并查集加 DFS 的题。
首先,看到判环这一个要求,我就自然而然地想到了并查集。于是我们可以先用边集数组把所有边存起来。接着遍历,每遍历一条边就判一次环,如果某条边插入后会出现环,则记录这个环的两个顶点 ,,不做插入。否则就插入并在并查集中合并。这个操作就相当于把环破坏成链。
接着,根据题意,没有环之后是一个树形结构,也就是两点之间路径唯一。所以可以使用 DFS 遍历从 到 的路径,这个即是环上的所有数。(因为从边 破开成链后 为起点, 为终点)
按照上述步骤实现即可。这里有个小技巧:因为题目的点的编号在 到 之间,所以我们在 DFS 遍历时只需记录访问过的节点,然后输出时就从 到 遍历并输出已经访问过的节点就可以实现按序输出,但需注意标记起点。
代码
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int fa[N]; //并查集数组
vector <int> g[N]; //vector 存图
struct edge
{
int fr, to;
} e[N]; //边集数组
bool vis[N]; //访问过的节点
int s, f, n; //起点,终点,题意中的 n
void dfs(int u) //DFS 遍历路径
{
if(u == f) //搜到了终点
{
for(int i = 1;i <= n;i++)
{
if(vis[i]) //访问过
{
cout << i << " ";
}
}
cout << endl;
exit(0); //直接退出,因为路径唯一
}
for(int i = 0;i < g[u].size();i++) //遍历邻接节点
{
int v = g[u][i];
if(!vis[v]) //如果未访问
{
//访问该节点
vis[v] = true;
dfs(v);
//回溯
vis[v] = false;
}
}
}
int find(int x) //并查集查找祖先函数
{
return (fa[x] == x ? x : fa[x] = find(fa[x]));
}
void merge(int i, int j) //并查集合并函数
{
fa[find(j)] = find(i);
}
int main()
{
cin >> n;
for(int i = 1;i <= n;i++) fa[i] = i; //并查集初始化
s = f = 0; //初始化起点、终点
int d = 0; //边数
for(int i = 1;i <= n;i++)
{
int x, y;
cin >> x >> y;
e[++d].fr = x; //记录边 (u, v) 的 u
e[d].to = y; //记录 v
}
for(int i = 1;i <= d;i++)
{
if(find(e[i].fr) == find(e[i].to)) //如果产生环
{
//记录起点、终点
s = e[i].fr;
f = e[i].to;
}
else
{
g[e[i].fr].push_back(e[i].to); //插入边
g[e[i].to].push_back(e[i].fr);
merge(e[i].fr, e[i].to); //合并
}
}
vis[s] = true; //标记起点
dfs(s); //DFS 遍历路径
return 0;
}

浙公网安备 33010602011771号