2105. [NOIP2015] 信息传递
【题目描述】
有n个同学(编号为1到n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?
给定一个有向图,求最小的环长度,可用DFS解决。
gyx教给我的显式栈SpecialFastDFS,效果拔群,成功屠榜
#include<cstdio>
#include<iostream>
#include<stack>
#define COGS
using namespace std;
int ans=2001000,n;
int t[2001000],vis[2001000];
stack<int> s;
inline int read(){//快速读入
int x,f=1;
char ch;
while(ch=getchar(),!isdigit(ch)){
if(ch=='-')f=-1;
if(ch==EOF)return -1;
}
x=ch-48;
while(ch=getchar(),isdigit(ch))x=x*10+ch-48;
return x*f;
}
int work(int x){//DFS:栈中元素如果再次被访问,则它在环上
int num=0;
s.push(x);
while(!s.empty()){
int te=s.top();
if(!vis[te]){//未访问过的节点,入栈,涂成灰色,用-1标记
s.push(t[te]);
vis[te]=-1;
}
else if(vis[te]==-1){//访问过一次的节点,涂成黑色,用1标记,同时也是环的一部分,答案+1
s.pop();vis[te]=1;num++;
while(vis[s.top()]!=1){//
vis[s.top()]=1;
s.pop();
num++;
}
s.pop();
while(!s.empty()){
vis[s.top()]=1;
s.pop();
}
}
else if(vis[te]==1){//把访问过两次的节点全出栈
while(!s.empty()){
vis[s.top()]=1;
s.pop();
}
}
}
return num;
}//Copyright © 2016 Gyx.
int main()
{
#ifdef COGS
freopen("2015message.in","r",stdin);
freopen("2015message.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;i++)t[i]=read();//仅记录边的连通关系
for(int i=1;i<=n;i++)if(!vis[i]){//如果vis[i]==1说明已经访问过且已出栈,不再进行DFS
int h=work(i);
if(h)ans=min(ans,h);
}
cout<<ans;
}
关于节点的颜色,详见算法导论