Input
* Line 1: Two space-separated integers, N and M
Output
* Line 1: A single integer that is the number of cows who are considered popular by every other cow.
Sample Input
3 3
Sample Output
1
Hint
Cow 3 is the only cow of high popularity.
Source
USACO 2003 Fall
题目大意:每头奶牛都希望自己成为最欢迎的那头牛。给你N头牛,M个崇拜关系(A,B)。
意思是牛A崇拜牛B。特别是,如果牛A崇拜牛B,牛B崇拜牛C,那么牛A也崇拜牛C。那么
问题来了:请计算出被所有牛崇拜的牛的个数。
思路:刚学的Kosaraju算法。考虑这道题,把崇拜关系(A,B)看做是一条有向边,并且,
我们发现牛的崇拜关系具有传递性。那么只要牛A有一条路径连向牛B,就可以判定牛A
崇拜牛B。于是,被所有牛崇拜的牛就是所有的点都存在一条路径连向它的有向路径。下
边简述下Kosaraju算法:
(1)对原图进行第一遍深度优先遍历,记录下每个节点的离开时间num[i]
(2)对原图的反向边构成的图进行第二遍深度优先遍历,从步骤(1)中离开时间最晚的点开
始。第(2)步中每搜索到一棵树都是一个强连通分量。用Hash[]把同一连通分量上的点缩
成一个点。
(3)缩点之后的图就构成了DAG(有向无环图),树的个数就是强连通分量的个数。
这道题中,将原图强连通分量缩点后构成了DAG,那么如果新图中出度为0的点只有一个,
则有解,为该出度为0的点的强连通分量中点的个数。若出度为0的点的个数不止一个,那
么无解。因为至少有两头牛互相不崇拜。
参考博文:http://blog.csdn.net/chang_mu/article/details/38709047
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int MAXN = 10010; const int MAXM = 50050; struct EdgeNode { int to; int next1; int fr; int next2; }Edges[MAXM]; int Head1[MAXN],Head2[MAXN],vis[MAXN]; int num[MAXN],Hash[MAXN],Count[MAXN],outdegree[MAXN]; int id; void AddEdges(int u,int v) { Edges[id].to = v; Edges[id].next1 = Head1[u]; Head1[u] = id; Edges[id].fr = u; Edges[id].next2 = Head2[v]; Head2[v] = id++; } //DFS第一遍,求出记录每个节点离开时间num[i] void DfsOne(int cur,int& sig) { vis[cur] = 1; for(int i = Head1[cur]; i != -1; i = Edges[i].next1) { if( !vis[Edges[i].to] ) DfsOne(Edges[i].to,sig); } num[++sig] = cur; } //DFS第二遍,求出双联通分量 void DfsTwo(int cur,int sig) { vis[cur] = 1; Hash[cur] = sig; //Hash用来将同一个联通分量中的点缩成一个点 Count[sig]++; for(int i = Head2[cur]; i != -1; i = Edges[i].next2) { if( !vis[Edges[i].fr]) DfsTwo(Edges[i].fr,sig); else if(Hash[Edges[i].fr] != Hash[cur]) //outdegree判断缩点后新图各点是否有出度 outdegree[Hash[Edges[i].fr]] = 1; } } int Kosaraju(int N) { int sig = 0,ans; memset(vis,0,sizeof(vis)); for(int i = 1; i <= N; ++i) if( !vis[i] ) DfsOne(i,sig); memset(vis,0,sizeof(vis)); memset(Count,0,sizeof(Count)); memset(outdegree,0,sizeof(outdegree)); int i = sig; sig = 0; for(; i >= 1; --i) if( !vis[num[i]]) DfsTwo(num[i],++sig); int temp = 0; for(int i = 1; i <= sig; i++) //新图只有一个点出度为0才算有解 if(!outdegree[i]) { temp++; ans = Count[i]; } //printf("$%d ",temp); if(temp == 1) return ans; else return 0; } int main() { int N,M,u,v; while(~scanf("%d%d",&N,&M)) { id = 0; memset(Head1,-1,sizeof(Head1)); memset(Head2,-1,sizeof(Head2)); for(int i = 0; i < M; ++i) { scanf("%d%d",&u,&v); AddEdges(u,v); } int ans = Kosaraju(N); printf("%d\n",ans); } return 0; }