http://poj.org/problem?id=2186
题意:有n头奶牛,给出m对奶牛之间的关系(a,b),表示a欢迎b。欢迎关系是单向可传递的,并且每个奶牛都是欢迎自己的。求出被所有奶牛欢迎的奶牛的数量。
思路:求出所有的连通分量(Tarjan算法),然后求出每个连通分量的出度,如果出度为0的连通分量大于一个,则该图不连通,输出0;如果出度为0的连通分量只有一个,则在该连通分量里的点即为受所有奶牛欢迎的奶牛,输出该连通分量里的点数即可。
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <stack> 5 const int N=100010; 6 using namespace std; 7 struct node 8 { 9 int u,v,next; 10 } edge[N]; 11 //dfn[i]表示点i的深度优先数; 12 int dfn[N],low[N],head[N];//low[i]表示点i可到达的最低深度优先数 13 int Conn_num[N],out[N]; //Conn_num[i]表示点i所属的连通分量的编号; 14 int n,m,cnt,dfs_clock,Conn_cnt; 15 stack<int>S; 16 17 void init() 18 { 19 cnt = 0; 20 dfs_clock = 0; 21 Conn_cnt = 0; 22 memset(dfn,0,sizeof(dfn)); 23 memset(low,0,sizeof(low)); 24 memset(out,0,sizeof(out)); 25 memset(Conn_num,0,sizeof(Conn_num)); 26 memset(head,-1,sizeof(head)); 27 } 28 void add(int u,int v) 29 { 30 edge[cnt].u = u; 31 edge[cnt].v = v; 32 edge[cnt].next = head[u]; 33 head[u] = cnt++; 34 } 35 36 void dfs(int u)//Tarjan算法 37 { 38 dfn[u]=low[u]=++dfs_clock//设定初值 39 S.push(u);//将节点u压入栈中 40 for (int i = head[u]; i!=-1; i=edge[i].next)//遍历u的临接点 41 { 42 int v = edge[i].v; 43 if (!dfn[v])//如果改点的深度优先数为0(即没有搜索过) 44 { 45 dfs(v);//继续向下找 46 low[u] = min(low[u],low[v]);//回溯过程中计算low[]的值 47 } 48 else if(!Conn_num[v])//v不在连通分量中,即v还在栈中 49 { 50 low[u] = min(low[u],dfn[v]); 51 } 52 } 53 if (dfn[u]==low[u])//找到一个连通分量 54 { 55 ++Conn_cnt;//连通分量计数 56 while(1)//将栈里属于同一个连通分量的点弹出 57 { 58 int v = S.top(); 59 S.pop(); 60 Conn_num[v] = Conn_cnt;//表示点v在第Conn_cnt个连通分量里 61 if (v==u) 62 break; 63 } 64 } 65 } 66 int main() 67 { 68 int u,v; 69 while(~scanf("%d%d",&n,&m)) 70 { 71 init(); 72 for (int i = 0; i < m; i++) 73 { 74 scanf("%d%d",&u,&v); 75 add(u,v); 76 } 77 for (int i = 1; i <= n; i++) 78 { 79 if (!dfn[i]) 80 dfs(i); 81 } 82 for (int i = 1; i <= n; i++) 83 { 84 for (int j = head[i]; j!=-1; j=edge[j].next)//遍历所有与i点相邻的点 85 { 86 int v = edge[j].v; 87 if (Conn_num[i]!=Conn_num[v])//如果i点与v点相连但是属于不同的连通分量,说明这两个连通分量之间存在有向边 88 out[Conn_num[i]]++; //i所属的连通分量的出度+1 89 } 90 } 91 int sum=0,pos=0; 92 for (int i = 1; i <= Conn_cnt; i++)//遍历所有的连通分量 93 { 94 if (!out[i]) 95 { 96 sum++;//计算出度等于0的连通分量的个数 97 pos = i;//标记出度等于0的连通分量的编号 98 } 99 } 100 if (sum!=1)//图不连通 101 printf("0 "); 102 else //出度等于0的连通分量只有一个 103 { 104 int ans = 0; 105 for (int i = 1; i <= n; i++) 106 { 107 if(Conn_num[i]==pos)//计算该连通分量(即出度为0的连通分量)中的点的个数 108 ans++; 109 } 110 printf("%d ",ans); 111 } 112 } 113 return 0; 114 }
关于Tarjan算法的介绍:https://www.byvoid.com/blog/scc-tarjan/