原题
http://poj.org/problem?id=2186
题目大意
题目会给一个牛的有向图,A→B意味着A认为B流行,如果A→B,B→C,则A和B都认为C流行,要求求出这群牛中被所有牛认为流行的牛的数.
题目分析
这道题中会存在一些情况例如 A→B,B→C,C→A这种,就是A B C强连通,所以需要把这些强连通的点集压缩成一个点并进行拓扑排序,然后只要找序列中的最后一个点,用dfs检查一遍是否能跑遍全图,是则输出该点被压缩的数量,否则输出0.找强连通可以先用dfs扫一下有向图(确保所有点都被该dfs扫过),再将地图上的所有路反向,再用dfs从上次dfs的第一个开始,扫遍所有的点,这次每扫一次dfs都能求解出一个强连通分量,因为路方向后,只有强连通里的点不受影响,各个强连通分量都会被隔绝,因此可以找出所有的强连通分量.
代码
1 #include <cstdio> 2 #include <cmath> 3 #include <iostream> 4 #include <cstring> 5 #include <algorithm> 6 #include <vector> 7 #include <string> 8 #include <utility> 9 #include <queue> 10 #include <stack> 11 #include <map> 12 const int INF=0x3f3f3f3f; 13 using namespace std; 14 15 vector<int> G[10001],rG[10001]; //G是放输入边的,rG是放反向后的边的 16 vector<int> vs; //用来存第一次dfs 点的遍历顺序 17 bool vis[10001]; //检查点是否扫过 18 int cmp[10001]; //表示拓扑排序中的序号 19 20 void add(int from,int to) //添加边 21 { 22 G[from].push_back(to); 23 rG[to].push_back(from); 24 } 25 26 void dfs(int s) //第一次dfs 27 { 28 vis[s]=true; 29 for(int i=0;i<G[s].size();i++) 30 if(!vis[G[s][i]]) dfs(G[s][i]); 31 vs.push_back(s); 32 } 33 34 void rdfs(int s,int k) //第二次dfs k是拓扑排序序号 35 { 36 vis[s]=true; 37 cmp[s]=k; 38 for(int i=0;i<rG[s].size();i++) 39 if(!vis[rG[s][i]]) rdfs(rG[s][i],k); 40 } 41 42 int main() 43 { 44 int n,m; 45 cin>>n>>m; 46 while(m--) 47 { 48 int a,b; 49 scanf("%d%d",&a,&b); 50 add(a,b); 51 } 52 53 for(int i=1;i<=n;i++) 54 if(!vis[i]) dfs(i); 55 56 memset(vis,0,sizeof(vis)); 57 int k=0; 58 59 for(int i=vs.size()-1;i>=0;i--) 60 if(!vis[vs[i]]) rdfs(vs[i],k++); 61 62 int u,ans=0; //找出拓扑序列最后一个点 63 for(int i=1;i<=n;i++) 64 if(cmp[i]==k-1) u=i,ans++; 65 66 //检查最后一个强连通分量上的一个点能否经过所有的点(即被所有牛认为流行) 67 memset(vis,0,sizeof(vis)); 68 rdfs(u,0); 69 for(int i=1;i<=n;i++) 70 if(!vis[i]) 71 { 72 ans=0; 73 break; 74 } 75 cout<<ans<<endl; 76 return 0; 77 }