蓝桥杯:消息传递

有n个同学( 编号为1到n)正在玩一个信息传递的游戏。

在游戏里每人都有一个固定的信息传递对象,其中,编号为n的同学的信息传递对象是编号为Ti的同学。游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象( 注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。 当有人从别人口中得知自己的生日时, 游戏结束。 请问该游戏一共可以进行几轮?

输入格式

输入共2行。

第1行包含1个正整数n(2≤n≤200000),表示n个人。

第2行包含n个用空格隔开的正整数T1,T2,⋯,Tn,其中第i个整数Ti表示编号为i的同学的信息传递对象是编号为Ti的同学, Ti≤n且Ti≠i。数据保证游戏一定会结束。

对于50%的数据n≤2000。

输出格式

输出共1行,包含1个整数,表示游戏一共可以进行多少轮。

样例输入

5
2 4 2 3 1

样例输出

3

解题思路:根据题意先画一个消息传递的过程图,大概是这个样子:(利用题目的样例)

很容易能分析出,游戏每进行一轮,自己的信息都会沿着箭头走一步,当自己的信息传递到自己这里时,在图中就会形成一个环,所以这道题可以转化为,求有向图中的最小环问题,下面说一下利用带权并查集解决求最小环的思路;

在我的理解中,这道题利用并查集的过程大概是这个样子的:

1.先初始化,每个点都是自己的父节点

     

2.从第一个点开始,将样例中所说的点依次加入并查集

这里当进行到i=4的时候,4应该传递消息给3,把3加入并查集这时发现3在并查集,这时就找到了一个环,这个环的长度是3

3.删掉形成环的那一条边,继续寻找下一个环

4.遍历完所有的点,找到最小的环,此题答案为2中找到的环,答案为3

 

AC代码

#include<iostream>
#include<string>
using namespace std;

int f[200005],size[200005],a[200005];
int ans=2000000;

//寻找x根节点 同时进行路径压缩 
int get(int x){
	if(f[x]==x) return x;
	int t=get(f[x]);
	size[x]+=size[f[x]];
	f[x]=t;
	
	return f[x];
}
//合并两个并查集 
void merge(int a,int b){
	int ta=get(a);
	int tb=get(b);
	if(ta!=tb){
		f[ta]=tb;
		size[ta]=size[b]-size[a]+1;
	}else{
		ans=min(ans,size[b]+1);/*寻找最小的距离*/
	}
}
int main(){
    int n;
    
    cin>>n;
    for(int i=1;i<=n;i++){
    	f[i]=i;/*自己的父亲就是自己*/
    	size[i]=0;/*每个点到自己的距离为0*/
    	cin>>a[i];/*a数组存储的是同学i将要传递的给a[i]*/
    }
    for(int i=1;i<=n;i++){
    	merge(i,a[i]);//并查集加入(i,a[i])边 
    }
    cout<<ans<<endl;
    
	return 0;
}

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值