1. 程式人生 > >【NOIP2015】資訊傳遞 CODE[VS] 4511

【NOIP2015】資訊傳遞 CODE[VS] 4511

題目描述 Description
有個同學(編號為 1 到)正在玩一個資訊傳遞的遊戲。在遊戲裡每人都有一個固定的資訊傳遞物件,其中,編號為的同學的資訊傳遞物件是編號為的同學。遊戲開始時,每人都只知道自己的生日。之後每一輪中,所有人會同時將自己當前所知的生日資訊告訴各自的資訊傳遞物件(注意:可能有人可以從若干人那裡獲取資訊,但是每人只會把資訊告訴一個人,即自己的資訊傳遞物件)。當有人從別人口中得知自己的生日時,遊戲結束。請問該遊戲一共可以進行幾輪?

輸入描述 Input Description
輸入共 2行。

第 1行包含1個正整數n,表示n個人

第 2 行包含n 個用空格隔開的正整數T1 ,T 2 ,……,Tn , 其中第i個整數Ti表示編號為i的同學的資訊傳遞物件是編號為 T i 的同學,Ti≤n 且 Ti≠i。

資料保證遊戲一定會結束。

輸出描述 Output Description
輸出共 1行,包含 1個整數,表示遊戲一共可以進行多少輪。

樣例輸入 Sample Input
5

2 4 2 3 1

樣例輸出 Sample Output
3

資料範圍及提示 Data Size & Hint

【輸入輸出樣例 1 說明】

遊戲的流程如圖所示。當進行完第 3 輪遊戲後,4 號玩家會聽到 2 號玩家告訴他自己的生日,所以答案為 3。當然,第 3 輪遊戲後,2 號玩家、3 號玩家都能從自己的訊息來源得知自己的生日,同樣符合遊戲結束的條件。

對於 30%的資料, N ≤ 200;

對於 60%的資料, N ≤ 2500;

對於 100%的資料, N ≤ 200000。

題解:

我們首先把題目抽象成一張帶有環的有向圖,當然有些點可能指向其他點但是不會被其他點指向,所以說這些人一定不會知道自己的生日,那麼是不是這些點就可以說不存在呢?顯然是可以的。而且如果一個點被多個點指向,但這多個點沒有任何一個被指向,也就說明這一個點也是無解的。
也就是說,只要有一個點在遊戲未結束的時候的入度為零,它一定是不符合答案的。

先另開一個數組記錄一下每個點的入度。
我們利用一個佇列存一下入度為零的點,如果有一個點的入度為零,我們讓他入隊。如果佇列非空,把當前隊首元素指向點的出度減去一,從佇列中彈出,也就實現了刪去該點的操作,一直pop下去,就把所有初始狀態下入度為零的點刪去。
但是還沒完,刪除了若干個點後,我們可能會得到新的入度為零的點,這樣的話我們再進行剛才的操作就可以了。最後我們一定會得到一個環,從任意點不斷地傳遞,知道我們訪問到已經訪問過的點為止。

程式碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 200000 + 10;
int n,to[maxn],tot,e[maxn];
bool vis[maxn]; 
queue<int>q;
int main()
{
    int ans = 2147483647;
    scanf("%d",&n);
    for(int i = 1;i <= n; i ++)
    {
        scanf("%d",&to[i]);
        e[to[i]]++;
    }
    for(int i = 1;i <= n; i ++)
    {
        if(e[i] == 0)
        {
            q.push(i);
            vis[i] = 1;
        }
    }
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        e[to[u]] --;
        if(e[to[u]] == 0)
        {
            q.push(to[u]);
            vis[to[u]] = 1;
        }
    }
    for(int i = 1;i <= n; i ++)
    {
        if(!vis[i])
        {
            vis[i] = 1;
            int sum = 1,j = to[i];
            while(!vis[j])
            {
                vis[j] = 1;
                j = to[j];
                sum ++;
            }   
            if(ans > sum) ans = sum;
        }
    }
    printf("%d\n",ans);
    return 0;
}

這道題還有DFS和Tarjan的做法
這裡挖個坑
等用那兩種方法做出來後填坑
To Be Continued…