noip2015 信息傳遞


P2661 信息傳遞

    • 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 個整數,表示游戲一共可以進行多少輪。

 

輸入輸出樣例

輸入樣例#1:
5
2 4 2 3 1
輸出樣例#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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM