1. 程式人生 > >洛谷P2661 資訊傳遞 【tarjan求最小環】

洛谷P2661 資訊傳遞 【tarjan求最小環】


題目描述

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