【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
52 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…