[NOIpTG2015]信息传递——[缩点]

【题目描述】

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

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

【输入格式】

共 2 行。
第 1 行包含1个正整数 n ,表示 n 个人。
第 2 行包含 n 个用空格隔开的正整数 T1,T2…Tn ,其中第 i 个整数 Ti 表示编号为 i 的同学的信息传递对象是编号为 Ti 的同学, Ti ≤n 且 Ti≠i 。

【输出格式】
1 个整数,表示游戏一共可以进行多少轮。

【样例输入】
5
2 4 2 3 1

【样例输出】
3

【题意分析】

这道题的解法很多,读完题目并分析后,我的第一反应是tarjan。传递关系可以看成一条有向边,因为要求的是有人听到别人告诉他自己的生日,这等价于在图中寻找一个不小于1的强联通分量。

tarjan板子题,Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#define MAXN 200005
#define INF 1 << 29
using namespace std;
 
struct Front_Link_Star{
    int next,to;
}edge[MAXN];
 
int head[MAXN],dfn[MAXN],low[MAXN],Circle[MAXN]/*,DAG[MAXN],*/,Stack[MAXN];
int top,tag,ans,cnt,circle,n;
bool vis[MAXN];
 
inline void Add_Edge(int u){
    edge[++cnt].next=head[u];
    head[u]=cnt;
}
 
inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-')w=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
 
inline void tarjan(int now){  //tarjan开始啦
    dfn[now]=low[now]=++tag;
    vis[now]=1;Stack[++top]=now;
    for (register int i=head[now];i;i=edge[i].next){
        int target=edge[i].to;
        if (!dfn[target]){
            tarjan(target);
            low[now]=min(low[now],low[target]);
        }else if (vis[target])low[now]=min(low[now],low[target]);
    }
    if (dfn[now]==low[now]){
        int circle=0;
        while (int y=Stack[top--]){
            ++circle;
            vis[y]=0;
            if (now==y)break;
        }
        if (circle>1)ans=min(ans,circle);  
        //如果找到了大于1的强联通分量,ans记录最小值即为答案
    }
}
 
int main(){
    n=read();
    for (register int i=1;i<=n;i++){
        edge[i].to=read();
        Add_Edge(i);  //加有向边
    }
    tag=top=0;
    ans=INF;
    for (register int i=1;i<=n;i++)
        if (!dfn[i])tarjan(i);
    printf("%d",ans);
    return 0;
}

另外,这道题用Kruskal也可以做(说是Kruskal,其实就是并查集找环),初始化每一个点的父亲是他自己,加一条边就合并一下,如果发现当前点和要加的点父亲相同,就说明找到环了,暴力走一遍环路径取最小值即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值