题目描述:
有 n 个同学(编号为1到n) 正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 Ti的同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?
思路
把每个同学看成一个点,信息的传递就是在他们之间连有向边,游戏轮数就是求最小环。
这里我写的是用Tarjan求出所有的强连通分量,然后比较找出 >= 2 的最小环。 建议求最小环的时候直接在tarjan里面做, 如果求出所有的强连通分量然后求最小环的话,需要 O(scc * n)的时间,当边数较大时,可能会T掉;
AC代码
#include <bits/stdc++.h>
using namespace std;
#define MAX_N 2000010
#define INF 0x3f3f3f3f
int head[MAX_N], cnt = 0, idx = 1, dfn[MAX_N], low[MAX_N], in_stack[MAX_N], belong[MAX_N], scc = 0, ans = INF;
stack<int> q;
struct Node{
int to, next;
}edge[MAX_N];
void init()
{
memset(head, -1, sizeof(head));
cnt = 0;
idx = 1;
}
void add(int x, int y)
{
edge[cnt].to = y;
edge[cnt].next = head[x];
head[x] = cnt++;
}
void tarjan(int u)
{
low[u] = dfn[u] = idx++;
q.push(u);
in_stack[u] = 1;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(in_stack[v])
{
low[u] = min(low[u], dfn[v]);
}
}
int tmp_ans = 0;
if(low[u] == dfn[u])
{
scc++;
do
{
int k = q.top();
in_stack[k] = false;
belong[k] = scc;
q.pop();
tmp_ans++;
if(k == u)
break;
}
while(!q.empty());
}
if(tmp_ans >= 2)
ans = min(ans, tmp_ans);
}
int main()
{
init();
int n;
scanf("%d", &n);
for(int i = 1; i<=n; i++)
{
int x;
scanf("%d", &x);
add(i, x);
}
memset(dfn, 0, sizeof(dfn));
for(int i = 1; i<=n; i++)
{
if(!dfn[i])
{
tarjan(i);
}
}
printf("%d", ans);
return 0;
}