【洛谷】P2661 信息传递(并查集)

题目描述:

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

整体思路:

  • 把每个人视作一个点,并向他的信息传递对象连一条有向边构成一个有向图。每个人都有且仅有一个信息传递对象,同时这个信息传递对象不能是自己。那么这个有向图一定存在一个环。
  • 信息从自己出发又回到自己,必然是传递的路径形成了一个环。当有人得知自己得信息时就结束,说明要在这个图中寻找一个最小的环。

并查集解法:

  • 把每个信息传递对象当成是并查集构成的树结构里面的“父节点”( T i T_{i} Ti是i的父节点)。每次将i合并到 T i T_{i} Ti所在的集合中,并且为了记录这个直接的父子关系,此处的并查集不进行路径压缩。
  • 每次都先判断i和 T i T_{i} Ti是否在一个集合中,如果属于一个集合的话就说明存在一个环,更新一下环的边数。此时不用把它们合并。
  • 在find函数中,除了查询x的根节点之外还要记录x到根节点的距离,当发现环的时候这个距离就是环数。

在这里插入图片描述

#include<cstdio>
#include<iostream>
  using namespace std;
    int f[200100];
    / /查询x的根结点并记录到根结点的结点数(最终检测出环的时候,结点数就是环数)	
    int find(int x,int &cot){  
    if(x==f[x]) return x;   
    int r=x;
	while(r!=f[r]){
	cot++;
	r=f[r];	
	} 
	return r;
	}
    int main(){
    int n,i,j,ans=200100;
    scanf("%d",&n);
	for(i=1;i<=n;i++) f[i]=i;	
    for(i=1;i<=n;i++){
    int Ti,cot=1;
	scanf("%d",&y);	
    if(find(Ti,cot)==i) ans=min(ans,cot);	/ /Ti和i在同一个集合中说明形成环,更新环数
    else {
    f[i]=Ti;  / /没形成环时把i归到Ti所在集合
    }
	}	
    printf("%d",ans);	
    return 0;	
	} 

总结:

  • 我们知道并查集可以用来维护一个集合,故可以用并查集来检测无向图的连通性或是用并查集来检测无向图中的环:把连通的顶点放到一个集合中就表示它们连通。遍历一个无向图的每一条边,不断地把有边相连的点加入同一个集合,如果检测到某条边上的两个点在之前就已经属于一个集合,那么这个图就存在环。
  • 对于无向图,一般不能用并查集来检测环。因为并查集维护的东西一般是双向的。如下面这个无向图,a点有3条出边,而它的出边并不一定指向a,故无法用并查集来维护这样的信息。
    在这里插入图片描述
  • 那本题是一个无向图,为什么又可以用并查集做呢?原因就在于本题的特殊之处:每个点有且只有一条出边。故我们可以把每个信息传递对象当成是并查集构成的树结构里面的“父节点”( T i T_{i} Ti是i的父节点)。同时我们并没有对这个树结构进行路径压缩,所以当“父节点”指向“子节点”时就必然存在一个环。

上一篇博客:【leetcode】62. 不同路径 & 63. 不同路径 II (dp入门)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值