2661 信息传递(tarjan&拓扑)

2015 NOIP提高组 Day1 第二题

提交次数:3

题目描述

有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 个整数,表示游戏一共可以进行多少轮。

 

tarjan:

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<vector>
 4 #include<stack>
 5 using namespace std;
 6 vector<vector<int> > v;
 7 vector<int>lt[200010];
 8 stack<int>s;
 9 int dfn[200010];
10 int low[200010];
11 int belong[200010];
12 bool instack[200010]; 
13 int bcnt;
14 int n;
15 int index;
16 void tarjan(int x){
17     dfn[x] = low[x] = ++index;
18     s.push(x);
19     instack[x] = true;
20     int i;
21     for(i = 0; i < v[x].size(); i++){
22         int k = v[x][i];
23         if(!dfn[k]){
24             tarjan(k);
25             low[x] = min(low[x], low[k]);
26         }
27         else if(instack[k])
28             low[x] = min(low[x], dfn[k]);    
29     }
30     int k;
31     if(dfn[x]==low[x]){
32         bcnt++;
33         do{
34             k = s.top();
35             s.pop();
36             instack[k] = false;
37             lt[bcnt].push_back(k);
38         }while(k != x);
39     }
40 }
41 int main(){
42     cin>>n;
43     int i;
44     v.resize(n+1);
45     for(i = 1; i <= n; i++){
46         int x;
47         scanf("%d", &x);
48         v[i].push_back(x);
49     }
50     for(i = 1; i <= n; i++){
51         if(!dfn[i])
52             tarjan(i);
53     }
54     int ans = 1<<30;
55     for(i = 1; i <= bcnt; i++){
56         int x = lt[i].size();
57         if(x > 1) ans = min(ans, x);
58     }
59     cout<<ans<<endl;
60     return 0;
61 }

备注:

特殊时期就长话短说了。

没看出来这是一道求最小环的裸题,唉太差。我突然发现在之前写的tarjan求强连通分量的模板我没发在博客上,这个就当模板了。

犯了两个错误,第一个是v忘了resize然后就炸了。另一个是误以为在同一个强连通分量的结点编号都是连着的,然后就只得了10分。

第二次提交最后一个点超时,输入改成scanf就可以了。

哦对,还有就是要注意,强连通分量的大小最小也得是一个以上。

 

拓扑:

代码:

#include<iostream>
#include<queue> 
using namespace std;

struct node{
    int next;
    int in;
    bool book;
    node(){next = in = 0; book = 0;}
}p[200010];
int ans = 1<<30;
queue<node>q;
int n;

int main(){
    cin>>n;
    int i;
    for(i = 1; i <= n; i++){
        cin>>p[i].next;
        p[p[i].next].in++;
    }
    for(i = 1; i <= n; i++){
        if(!p[i].in){
            q.push(p[i]);
            p[i].book = true;
        }
    }
    while(!q.empty()){
        node k = q.front();
        q.pop();
        p[k.next].in--;
        if(p[k.next].in==0){
            q.push(p[k.next]);
            p[k.next].book = 1; 
        }
    }
    for(i = 1; i <= n; i++){
        if(!p[i].book){
            int j = i, count = 0;
            do{
                count++;
                p[j].book=1;
                j = p[j].next;
            }while(j!=i);
            ans = min(ans, count);
        }
    }
    cout<<ans<<endl;
    return 0;
}

备注:

“先拓扑,去掉图中不在环内的节点,再判断每个环的长度,最短的那个环的长度就是答案了!”

第一次写拓扑,参考某题解。一个点入度为0时不可能在环内。这里判断每个环长度,然后找最短的,用到了出度1的条件,决定了每个点只能在1个环内。

posted @ 2016-11-18 13:37  timeaftertime  阅读(375)  评论(0编辑  收藏  举报