CF1927F 题解
题意简述
给定一个无向图,边带权,找到最小的边权最小的环,输出这个最小的边权、环上点的个数和环上每个点的编号。
思路
回顾 Kruskal 求最小生成树的过程,若当前边所连的两个点在并查集上处于同一个集合,那么连上这条边就会产生环。
我们让边权降序排序,那么最后一个出现上述情况的边就是题目中要输出边权的边。
不升序排序看第一个出现这种情况的边,是因为可能因为没连边权大的边导致当前边没出现这种情况。
输出路径 dfs 即可,设刚才求出的边所连的两点分别是
Code
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 200005;
struct Edge{
int a, b, c;
}g[N];
int f[N];
struct EDGE{
int to, nxt;
}G[N << 1];
int h[N], idx;
bool vis[N];
int ans[N], tot, End;
bool cmp(Edge A,Edge B)
{
return A.c > B.c;
}
int find(int x)
{
if (x == f[x])
return x;
return f[x] = find(f[x]);
}
void add(int a,int b)
{
G[++idx].to = b, G[idx].nxt = h[a], h[a] = idx;
}
bool dfs(int u,int fa)
{
ans[++tot] = u;
if (u == End)
{
printf("%d\n", tot);
for (int i=1;i<=tot;i++)
printf("%d ", ans[i]);
putchar('\n');
return true;
}
vis[u] = true;
for (int i = h[u]; i; i = G[i].nxt)
{
int j = G[i].to;
if (j == fa || vis[j])
continue;
if (dfs(j, u))
return true;
}
tot --;
return false;
}
void work()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i=1;i<=m;i++)
scanf("%d%d%d", &g[i].a, &g[i].b, &g[i].c);
sort(g + 1, g + m + 1, cmp);
for (int i=1;i<=n;i++)
f[i] = i;
int mn;
for (int i=1;i<=m;i++)
{
int fa = find(g[i].a), fb = find(g[i].b);
if (fa == fb)
mn = i;
else
f[fa] = fb;
}
printf("%d ", g[mn].c);
for (int i=1;i<=n;i++)
h[i] = 0;
idx = 0;
for (int i=1;i<=m;i++)
{
add(g[i].a, g[i].b);
add(g[i].b, g[i].a);
}
for (int i=1;i<=n;i++)
vis[i] = false;
int u = g[mn].a, v = g[mn].b;
End = u;
tot = 0;
dfs(v, u);
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
work();
return 0;
}