[洛谷] P2661 信息传递 (简单图论)

题目描述

有 nn 个同学(编号为 11 到 nn )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 ii 的同学的信息传递对象是编号为 T_iTi 的同学。

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

输入格式

2行。

1行包含1个正整数 nn ,表示 nn 个人。

2行包含 nn 个用空格隔开的正整数 T_1,T_2,cdotscdots,T_nT1,T2,,Tn ,其中第 ii 个整数 T_iTi 表示编号为 ii 的同学的信息传递对象是编号为 T_iTi 的同学, T_i leq nTin 且 T_i eq iTii 。

题意:求一个有向图的最小环

输入:

5
2 4 2 3 1

输出:

3

思路:由于每一个人只有一个操作对象,那么这个有向图每一个连通分支必定有一个环

那么我的做法就是对于在所有标记为0的点开始DFS,对其中经过的点打上某一次DFS的标记,

在对某些遍历过的连通分支的标记为0的点进行操作时,直接跳出。

每次找到环之后计算出所用的轮数,直到所有的点的标记都不为0。预计时间复杂度为 O(n)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN=2e5+10;
int vis[MAXN];
int nx[MAXN],lp;
int cur[MAXN];
int ans=0x3f3f3f3f;

void dfs(int st)
{
    lp++;
    int cnt=0,t=st;
    while(vis[t]==0)
    {
        cur[t]=cnt;
        cnt++;
        vis[t]=lp;                            //做上趟数标记
        t=nx[t];
        if(vis[t]!=lp&&vis[t]!=0)      //发现是之前操作过的连通分支,则跳出      
            return ;
    }
    ans=min(ans,cnt-cur[t]);
}

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;++i)
        scanf("%d",&nx[i]);
    for(int i=1;i<=n;++i)
    {
        if(!vis[i])    dfs(i);
    }
    cout<<ans;
    return 0;
}                

判断是否属于同一连通分支也可以用并查集,但是这里地方太小,我写不下(滑稽)

等改天有时间了(?),就来写一下并查集的做法。(也许不咕)

学业繁忙,告辞


原文地址:https://www.cnblogs.com/JNzH/p/11253053.html