-
- 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; }