挑战程序设计原题。
感觉这个方法容易理解。
第一次dfs,先用一个时间戳来标记一下(时间戳就是寻访完这个店结束的时间),不是遍历的时间,
我们可以保证的是 最开始的点 时间戳最大。
然后 根据时间戳,从大到小进行第二次dfs(把图反向)
就可以得到k。
这个方法容易理解。
并且可以练习dfs。。。
#include <cstdio>
#include <vector>
#include <iostream>
#include <cstring>
/*双dfs求强联通分量。
当然用targin算法也可以,不过我感觉这个也挺好的,
具体思想是,先通过dfs来扫一下,再用vs一个数组打一个时间戳,
就是扫完点结束的事件(先扎到底,那个就是1,在找枝叶)
从时间戳最小的点开始扫,来计算强连通分量。
并且强连通分量会得到一个 拓扑排序,这个拓扑排序很好用。
先求强连通分量,然后对拓扑序最大的那个,
判定一下他是否和所有点都联通。
*/
using namespace std;
const int maxn=10009;
vector <int> vs;
vector <int>G[maxn];
vector <int>G2[maxn];
int tp[maxn];
bool used[maxn];
void add(int u,int v)
{ G[u].push_back(v);
}
void add1(int u,int v)
{ G2[u].push_back(v);
}
int dfs(int s)
{ used[s]=true;
for(int i=0;i<G[s].size();i++)
{ if(!used[G[s][i]])
dfs(G[s][i]);
}
vs.push_back(s);//
return 0;
}
int rdfs(int s,int k)//对反向图进行搜索
{ tp[s]=k; used[s]=true;
for(int i=0;i<G2[s].size();i++)
{ if(!used[G2[s][i]])
rdfs(G2[s][i],k);
}
return 0;
}
int main()
{ int m,n;
int a,b;
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
{scanf("%d%d",&a,&b);
add(a,b);
add1(b,a);//购置反向边。
}
//先bfs,对层数为2的在建一个边。
memset(used,false,sizeof(used));
for(int i=1;i<=m;i++)
if(!used[i])
dfs(i);
memset(used,false,sizeof(used));
// for(int i=0;i<vs.size();i++)
//cout<<vs[i]<<endl;
//for(int i=0;i<vs.size();i++)
// cout<<vs[i]<<endl;
int k=1;
for(int i=vs.size()-1;i>=0;i--)
{ if(!used[vs[i]])
{rdfs(vs[i],k);
//cout<<vs[i]<<endl;
k++;
}
}
k--;
int sum=0;
int v;
for(int i=1;i<=m;i++)
{ if(tp[i]==k)
{sum++;
v=i;
}
}
memset(used,false,sizeof(used));
rdfs(v,1);
for(int i=1;i<=m;i++)
{ if(!used[i])
{sum=0;
break;
}
}
cout<<sum<<endl;
return 0;
}