noip2015 信息传递
-
- 829通过
- 3.4K提交
- 题目提供者该用户不存在
- 标签图论线性结构2015NOIp提高组
- 难度普及/提高-
提交该题 讨论 题解 记录
最新讨论
- 自己电脑上用官方数据评测全…
- p党最后一个点超时 yyyyyyy…
- pascal党,只能拿70,最后三…
题目描述
有n个同学(编号为1到n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?
输入输出格式
输入格式:
输入共2行。
第1行包含1个正整数n表示n个人。
第2行包含n个用空格隔开的正整数T1,T2,……,Tn其中第i个整数Ti示编号为i
的同学的信息传递对象是编号为Ti的同学,Ti≤n且Ti≠i
数据保证游戏一定会结束。
输出格式:
输出共 1 行,包含 1 个整数,表示游戏一共可以进行多少轮。
输入输出样例
5 2 4 2 3 1
3
游戏的流程如图所示。当进行完第 3 轮游戏后, 4 号玩家会听到 2 号玩家告诉他自
己的生日,所以答案为 3。当然,第 3 轮游戏后, 2 号玩家、 3 号玩家都能从自己的消息
来源得知自己的生日,同样符合游戏结束的条件。
对于 30%的数据, n ≤ 200;
对于 60%的数据, n ≤ 2500;
对于 100%的数据, n ≤ 200000。
分析:去年参加Noip的时候什么都不会,这道题想了个非常奇怪的方法, 然后爆零了,其实这道题题意非常明确,就是让我们寻找一个最小环的长度,怎么求呢?也很简单,先把入度为0的点删除,然后把这个点指向的点的入度-1,如果入度为0,也删去,这样就只保留有用的点,那么从任意一个点开始,用vis数组记录是否被访问过,访问到一个新节点就累加计数器,然后就做出来了.bfs和dfs都可以.
#include <cstdio> #include <cstring> #include <queue> #include <iostream> #include <algorithm> using namespace std; int to[200010],vis[200010], rubian[200010]; int n,ans; queue <int> q; int main() { memset(to, 0, sizeof(to)); memset(vis, 0, sizeof(vis)); memset(rubian, 0, sizeof(rubian)); scanf("%d", &n); ans = n; for (int i = 1; i <= n; i++) { scanf("%d", &to[i]); ++rubian[to[i]]; } for (int i = 1; i <= n; i++) if (rubian[i] == 0) { q.push(i); vis[i] = 1; } while (!q.empty()) { int u = q.front(); q.pop(); --rubian[to[u]]; if (rubian[to[u]] == 0) { vis[to[u]] = 1; q.push(to[u]); } } int temp,j; for (int i = 1; i <= n; i++) { if (vis[i] == 0 && rubian[i] != 0) { vis[i] = 1; temp = 1; j = to[i]; while (!vis[j]) { vis[j] = 1; j = to[j]; temp++; } if (temp <= ans) ans = temp; } } printf("%d\n", ans); return 0; }
方法2:在有向图中秋最小环可以用到tarjan算法,可以理解为大小不为1的强连通分量,那么套tarjan模板,一旦遇到环就更新答案.
#include <cstdio> #include <stack> #include <cstring> #include <iostream> #include <algorithm> const int maxn = 200010; using namespace std; int n, head[maxn], nextt[maxn * 2], to[maxn * 2],tot,ans = 10000000000,pre[maxn],low[maxn],vis[maxn],dfsclock,sizee; stack <int> s; void tarjan(int u) { pre[u] = low[u] = ++dfsclock; s.push(u); for (int i = head[u]; i; i = nextt[i]) { int v = to[i]; if (!pre[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (!vis[v]) low[u] = min(low[u], pre[v]); } if (low[u] == pre[u]) { sizee++; int temp = 0; while (1) { int t = s.top(); s.pop(); temp++; vis[t] = sizee; if (t == u) { //printf("%d\n", temp); if (temp != 1) ans = min(temp, ans); break; } } } } void add(int x, int y) { tot++; to[tot] = y; nextt[tot] = head[x]; head[x] = tot; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { int t; scanf("%d", &t); add(i, t); } for (int i = 1; i <= n; i++) if (!pre[i]) tarjan(i); printf("%d\n", ans); //while (1); return 0; }