有向图——强连通分量

来源:互联网 发布:nginx配置文件位置 编辑:程序博客网 时间:2024/06/06 14:09

    有向图的强连通分量(strongly connected components)

在有向图G中,如果两个顶点vi,vj间(vi!=vj)有一条从vi到vj的路径,同时还有一条从vj到vi的路径(顶点相互可达),则称两个顶点强连通。如果有向图G的每对顶点都强连通,称G是个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量。

    求解强连通分量的算法主要有三种:Kosaraju,Tarjan,Gabow。下面介绍Tarjan算法。

    Tarjan算法基于图的深度遍历。定义dfn[u]是结点u的时间戳,low[u]为u和u的子树能够追溯到的最早的栈中结点的时间戳。

    low[u] = min{dfn[u], low[v](u,v为树枝边,u为v的父节点), dfn[v](u,v为指向栈中结点的后向边)};

    当dfn[u] == low[u]时,以u为根的搜索子树上所有结点是一个强连通分量。

 

关于Tarjan算法的详细介绍,参考:

http://hi.baidu.com/escorter2009/blog/item/f35951dc5bdb3de677c63826.html

 

POJ2186

题目大意:有n头牛,每头牛都希望自己受欢迎,输入a b表示a认为b受欢迎。要求出受其它所有牛欢迎的牛的个数。

解:首先求强连通分量,分量中每头牛都受分量中其它牛的欢迎。分量之间至多有一条有向边。那么显然需要找出出度为0的强连通分量,若有大于1的出度为0的强连通分量,这两个连通分量互相不认为对方受欢迎,显然个数为0;则有且仅有一个入读度为0的强连通分量时,该强连通分量的结点数即为解。

Cpp代码  收藏代码
  1. //出度为0的scc  
  2. #include <iostream>  
  3. const int MAX = 10002;  
  4. int n,m;  
  5. int low[MAX],dfn[MAX],seq;  
  6. bool inStack[MAX];  
  7. struct Edge  
  8. {  
  9.     int to;  
  10.     int next;  
  11. }e[50001];  
  12. int index[MAX],edgeNum;  
  13. int stack[MAX],top;  
  14. int belong[MAX];                    //belong[i]表示结点i属于的连通分量  
  15. int outdegree[MAX];   
  16. int cnt;                            //cnt为当前强连通分量的标记值  
  17.   
  18. int min(int x, int y)  
  19. {  
  20.     return x < y ? x : y;  
  21. }  
  22.   
  23. void addEdge(int from, int to)  
  24. {  
  25.     e[edgeNum].to = to;  
  26.     e[edgeNum].next = index[from];  
  27.     index[from] = edgeNum++;  
  28. }  
  29.   
  30. void Tarjan(int u)  
  31. {  
  32.     low[u] = dfn[u] = seq++;  
  33.     stack[top++] = u;                       //将结点u入栈  
  34.     inStack[u] = true;  
  35.     for(int i = index[u]; i != -1; i = e[i].next)  
  36.     {  
  37.         int w = e[i].to;  
  38.         if(dfn[w] < 0)  
  39.         {  
  40.             Tarjan(w);  
  41.             low[u] = min(low[u],low[w]);  
  42.         }  
  43.         else if(inStack[w])                 //如果节点w还在栈内  
  44.             low[u] = min(low[u],dfn[w]);  
  45.     }  
  46.     if(dfn[u] == low[u])                //u是强连通分量的根  
  47.     {  
  48.         int v;  
  49.         cnt++;  
  50.         //出栈,缩点  
  51.         do  
  52.         {  
  53.             top--;  
  54.             v = stack[top];  
  55.             inStack[v] = false;  
  56.             belong[v] = cnt;  
  57.         }while(u != v);  
  58.     }  
  59. }  
  60.   
  61. void solve()  
  62. {  
  63.     int i,j;  
  64.     for(i = 1; i <= n; i++)  
  65.         if(dfn[i] < 0)  
  66.             Tarjan(i);  
  67.     for(i = 1; i <= n; i++)  
  68.     {  
  69.         for(j = index[i]; j != -1; j = e[j].next)  
  70.         {  
  71.             if(belong[i] != belong[e[j].to])  
  72.                 outdegree[belong[i]]++;  
  73.         }  
  74.     }  
  75.     int num = 0;    //出度为0的连通分量的个数  
  76.     int who;        //哪个连通分量  
  77.     for(i = 1; i <= cnt; i++)  
  78.     {  
  79.         if(outdegree[i] == 0)  
  80.         {  
  81.             who = i;  
  82.             num++;  
  83.         }  
  84.     }  
  85.     if(num != 1)  
  86.         printf("0\n");  
  87.     else  
  88.     {  
  89.         int result = 0;  
  90.         for(i = 1; i <= n; i++)  
  91.             if(belong[i]==who)  
  92.                 result++;  
  93.         printf("%d\n",result);  
  94.     }  
  95. }  
  96.   
  97. int main()  
  98. {  
  99.     int i,j;  
  100.     int from,to;  
  101.     edgeNum = 0;  
  102.     seq = 0;  
  103.     top = 0;  
  104.     cnt = 0;  
  105.     memset(dfn,-1,sizeof(dfn));  
  106.     memset(index,-1,sizeof(index));  
  107.     memset(inStack,0,sizeof(inStack));  
  108.     memset(outdegree,0,sizeof(outdegree));  
  109.     scanf("%d %d",&n,&m);  
  110.     for(i = 0; i < m; i++)  
  111.     {  
  112.         scanf("%d %d",&from,&to);  
  113.         addEdge(from,to);  
  114.     }  
  115.     solve();  
  116.     return 0;  
  117. }  

 

POJ2553

题目大意:一个图由结点集v和边集E组成,现在要求出一个点集合:

    bottom(G)={v∈V|∀w∈V:(v→w)⇒(w→v)}(对于集合中的任意一点v和V中的任意一点w,如果v能到达w,那么w也能到达v)。

解:首先求强连通分量,联想到出度为0的scc(因为出度不为0的scc,不满足集合的定义)。

Cpp代码  收藏代码
  1. //出度为0的所有scc  
  2. #include <iostream>  
  3. const int MAX = 5005;  
  4. int n,m;  
  5.   
  6. struct Edge  
  7. {  
  8.     int to;  
  9.     int next;  
  10. }e[MAX*MAX];  
  11. int index[MAX],edgeNum;  
  12.   
  13. int low[MAX],dfn[MAX],seq;  
  14. int stack[MAX],top;  
  15. bool inStack[MAX];  
  16. int belong[MAX],outdegree[MAX],cnt;  
  17. int result[MAX],count;  
  18.   
  19. int min(int x, int y)  
  20. {  
  21.     return x < y ? x : y;  
  22. }  
  23.   
  24. void addEdge(int from, int to)  
  25. {  
  26.     e[edgeNum].to = to;  
  27.     e[edgeNum].next = index[from];  
  28.     index[from] = edgeNum++;  
  29. }  
  30.   
  31. void Tarjan(int u)  
  32. {  
  33.     low[u] = dfn[u] = seq++;  
  34.     stack[top++] = u;  
  35.     inStack[u] = true;  
  36.     for(int i = index[u]; i != -1; i = e[i].next)  
  37.     {  
  38.         int w = e[i].to;  
  39.         if(dfn[w] < 0)  
  40.         {  
  41.             Tarjan(w);  
  42.             low[u] = min(low[u],low[w]);  
  43.         }  
  44.         else if(inStack[w])                 //如果节点w还在栈内  
  45.             low[u] = min(low[u],dfn[w]);  
  46.     }  
  47.     if(low[u] == dfn[u])  
  48.     {  
  49.         int v;  
  50.         cnt++;  
  51.         do  
  52.         {  
  53.             top--;  
  54.             v = stack[top];  
  55.             inStack[v] = false;  
  56.             belong[v] = cnt;  
  57.         }while(u != v);  
  58.     }  
  59. }  
  60.   
  61. void solve()  
  62. {  
  63.     int i,j;  
  64.     for(i = 1; i <= n; i++)  
  65.         if(dfn[i] < 0)  
  66.             Tarjan(i);  
  67.     for(i = 1; i <= n; i++)  
  68.     {  
  69.         for(j = index[i]; j != -1; j = e[j].next)  
  70.         {  
  71.             if(belong[i] != belong[e[j].to])  
  72.                 outdegree[belong[i]]++;  
  73.         }  
  74.     }  
  75.     for(i = 1; i <= n; i++)  
  76.     {  
  77.         if(outdegree[belong[i]] == 0)  
  78.             result[count++] = i;  
  79.     }  
  80. }  
  81.   
  82. int main()  
  83. {  
  84.     int i,j;  
  85.     int from,to;  
  86.     while(true)  
  87.     {  
  88.         scanf("%d",&n);  
  89.         if(n==0)  
  90.             break;  
  91.         edgeNum = top = seq = count = cnt =0;  
  92.         memset(dfn,-1,sizeof(dfn));  
  93.         memset(index,-1,sizeof(index));  
  94.         memset(inStack,0,sizeof(inStack));  
  95.         memset(belong,0,sizeof(belong));  
  96.         memset(outdegree,0,sizeof(outdegree));  
  97.         scanf("%d",&m);  
  98.         for(i = 0; i < m; i++)  
  99.         {  
  100.             scanf("%d %d",&from,&to);  
  101.             addEdge(from,to);  
  102.         }  
  103.         solve();  
  104.         if(count == 0)  
  105.             printf("\n");  
  106.         else  
  107.         {  
  108.             for(i = 0; i < count-1; i++)  
  109.                 printf("%d ",result[i]);  
  110.             printf("%d\n",result[i]);  
  111.         }  
  112.     }  
  113.     return 0;  
  114. }  
0 0
原创粉丝点击