P2661 信息传递

文章描述了一个编号为1到n的同学进行的生日信息传递游戏,每人都有一个传递对象。游戏结束条件是某人得知自己的生日。关键在于找出由点和其传递对象组成的图中的最短环。通过使用并查集来确定环,并计算环的长度,以确定游戏的最少轮数。代码实现中,初始化并查集,然后通过合并节点并更新距离来找到最短环的长度。
摘要由CSDN通过智能技术生成

题意

有 n 个同学(编号为 1 到 n )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 T[i] 的同学。

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

思路

显然一个人能够得知自己的生日的前提是他的生日经过重重传递后又传递到了他耳里,即这个以这些人为点,它们与它们的信息传递对象所组成的边构成的图内存在环,而环的长度的最小值即为这个游戏的轮数。

问题转化为“每个点的出度均为1的图中环的长度的最小值”,那么我们就要知道什么样的时候能组成环,可以想到并查集(我们将环的第一个点给环作为它的代号,此时这个环可以看作以这个点为父节点的并查集),而这个环的长度可以比拟为父节点的生日先跑到了一个节点,然后从一个节点跑到另一个节点,最后再跑回来,那么加起来就是 两个节点到父节点的距离之和 + 1(两个节点之间的距离是1) 。

现在还差一个问题:怎么得到点到父节点之间的距离?如果因为父节点不相同要接入别的父节点时直接用另一个点的距离+1刷新;而当寻找父节点时因为会直接刷新父节点,所以需要用一个变量来记录旧的父节点,然后子节点到父节点的距离就是旧父节点到父节点的距离+子节点到旧父节点的距离了!

思路已经明朗,代码开始实现!

代码

#include <iostream>
#define MAXN 200005
using namespace std;
int n, T[MAXN], f[MAXN], dist[MAXN];
int ans = 888888888;

int find_f(int x) {
    if (f[x] == x)
        return x;
    int oldFather = f[x];
    f[x] = find_f(f[x]);
    dist[x] += dist[oldFather];
    return f[x];
}

void init() {
    for (int i = 1; i <= n; ++i)
        f[i] = i;
}

void solve() {
    cin >> n;
    init();
    for (int i = 1; i <= n; ++i) {
        cin >> T[i];
        int uu = find_f(i);
        int vv = find_f(T[i]);
        if (uu != vv) {
            f[uu] = vv;
            dist[i] = dist[T[i]] + 1;
        } else {
            ans = min(ans, dist[i] + dist[T[i]] + 1);
        }
    }
    cout << ans;
}


int main() {

    ios::sync_with_stdio(0);
    cin.tie(0);

    solve();

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值