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个环内。