题解:CF1927F(Microcycle)

题解:CF1927F(Microcycle)

一、 理解题意

1. 题目链接

(1) CF链接

CodeForces-Contest-1927F

(2) LG链接

洛谷-Problem-CF1927F

2. 题目翻译

(1) 题目描述

给定一个 n n n 个点、 m m m 条边的简单无向图(每条边表示为 u u u v v v w w w,表示 u u u v v v 之间有一条权值为 w w w 的边),边带权,不一定连通,保证存在至少一个简单环(没有重点、重边的环)。定义一个简单环的权值为环上边权最小的边的边权,求出该图中权值最小的简单环。

(2)输入输出
①输入格式

t t t 组数据,每组数据先输入 n n n m m m,随后输入 m m m 条边,每条边分别输入 u u u v v v w w w

②输出格式

对于每组数据,输出:
第一行:找出的环的权值,空格隔开,找出的环的定点数;
第二行:按顺序输出环上的每个点的编号。

(3) 数据范围

1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104
3 ≤ n ≤ m ≤ min ⁡ ( n ⋅ ( n − 1 ) 2 , 2 ⋅ 1 0 5 ) 3 \le n \le m \le \min(\frac{n\cdot(n - 1)}{2}, 2 \cdot 10^5) 3nmmin(2n(n1),2105)
1 ≤ u , v ≤ n 1 \le u, v \le n 1u,vn, u ≠ v u \ne v u=v, 1 ≤ w ≤ 1 0 6 1 \le w \le 10^6 1w106
m m m 总和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105

二、 分析思路

先考虑求出最小权值。
类似克鲁斯卡尔的最小生成树,我们把每条边从大到小排序,顺序遍历每条边,对于每条边,我们做如下操作:
判断端点是否已经在一个集合内,如果在,就说明通过这条边已经能够构成一个环,记录答案编号为现在编号。
随后把两个端点所在集合合并。
这里用并查集。
随后根据找到的边,从一个端点跑 d f s dfs dfs 直到跑到另一个端点(当然不能走这条边本身),维护一下路径即可。
具体看代码。

三、推测时间

O ( n + m ) O(n+m) O(n+m)
并查集的代价忽略不计。

四、实现代码

#include<bits/stdc++.h>
#define M 220000
#define N 220000
using namespace std;
int m=0,n=0,t=0,u=0,v=0,w=0;
struct Edge{
	int u,v,w;
	bool operator<(Edge ot){
		return w>ot.w;
	}
};
Edge e[M]={};
vector<pair<int,int>>edge[N]={};
int fa[N]={},st[N]={},top=0;
bool hg[N]={};
int get_fa(int num);
bool dfs(int mb,int node,int s);
bool judge(int x,int y);
void merge(int x,int y);
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			edge[i].clear();
			fa[i]=i;
			hg[i]=false;
		}
		for(int i=1;i<=m;i++){
			scanf("%d%d%d",&u,&v,&w);
			edge[u].push_back({v,w});
			edge[v].push_back({u,w});
			e[i]={u,v,w};
		}
		sort(e+1,e+1+m);
		int ans=-1;
		for(int i=1;i<=m;i++){
			int u=e[i].u,v=e[i].v;
			if(judge(u,v)==false){
				merge(u,v);
			}else{
				ans=i;
			}
		}
		printf("%d ",e[ans].w);
		hg[e[ans].v]=true;
		top=1;
		st[top]=e[ans].v;
		dfs(e[ans].u,e[ans].v,1);
	}
	return 0;
}
int get_fa(int num){
	if(fa[num]==num){
		return num;
	}
	fa[num]=get_fa(fa[num]);
	return fa[num];
}
bool dfs(int mb,int node,int s){
	if(node==mb){
		printf("%d\n",s);
		for(int i=1;i<=s;i++){
			printf("%d ",st[i]);
		}
		printf("\n");
		return true;
	}
	for(pair<int,int>i:edge[node]){
		if(hg[i.first]==false){
			if(i.first==mb&&s==1){
				continue;
			}
			hg[i.first]=true;
			top++;
			st[top]=i.first;
			if(dfs(mb,i.first,s+1)==true){
				return true;
			}
			top--;
		}
	}
	return false;
}
bool judge(int x,int y){
	int fx=get_fa(x),fy=get_fa(y);
	if(fx==fy){
		return true;
	}
	return false;
}
void merge(int x,int y){
	int fx=get_fa(x),fy=get_fa(y);
	fa[fx]=fy;
	return;
}
  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值