Popular Cows

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\n");
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\n",ans);
111         }
112     }
113     return 0;
114 }
View Code

关于Tarjan算法的介绍:https://www.byvoid.com/blog/scc-tarjan/

 

posted @ 2014-02-13 21:38  N_ll  阅读(145)  评论(0编辑  收藏  举报