tarjan算法 求解强联通分量 POJ_2186_Popular Cows

来源:互联网 发布:淘宝贷款在哪里 编辑:程序博客网 时间:2024/05/29 04:46



如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。

下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。
Tarjan算法是用来求有向图的强连通分量的。求有向图的强连通分量的Tarjan算法是以其发明者Robert Tarjan命名的。Robert Tarjan还发明了求双连通分量的Tarjan算法。

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。
当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。

接下来是对算法流程的演示。
从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。



返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。



返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1。


继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以LOW[2]=LOW[4]=1。返回1后,发现DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量{1,3,4,2}。


至此,算法结束。经过该算法,求出了图中全部的三个强连通分量{1,3,4,2},{5},{6}。
可以发现,运行Tarjan算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(N+M)。

/*
Popular Cows
Time Limit: 2000MS        Memory Limit: 65536K
Total Submissions: 29227        Accepted: 11825

Description
Every cow's dream is to become the most popular cow in the herd(兽群). In a herd of N (1 <= N <= 10,000) cows,
 you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow
 B is popular. Since popularity(普及) is transitive(及物的), if A thinks B is popular and B thinks C is popular,
 then A will also think that C is
popular, even if this is not explicitly(明确地) specified(指定) by an ordered pair in the input(投入). Your task is to
compute the number of cows that are considered popular by every other cow.

Input
* Line 1: Two space-separated integers(整数), N and M

* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

Output
* Line 1: A single integer that is the number of cows who are considered popular by every other cow.

Sample Input

3 3
1 2
2 1
2 3

6 8
1 3
3 5
5 6
1 2
2 4
4 6
4 1
3 4
Sample Output

1

1
*/
题意:

有n只牛,A认为B很厉害,B认为C很厉害。
给你M个关系,求大家都认为它很厉害的牛有几只。
如果 A认为B很厉害,B认为C很厉害,那么A认为C也很厉害。

思路:

判断给出的有向图中出度为0的联通分量的个数,如果为1就输出联通分量中的点的数目,否则输出0.

#include <stdio.h>#include <string.h>#include <algorithm>using namespace std; const int Max_N = 10002;struct Node{int to;int next;}edge[Max_N * 10];int n,m;int head[Max_N];int dfn[Max_N];int low[Max_N];int _stack[Max_N];int num[Max_N];int degree[Max_N];bool vis[Max_N];int cou,time,ans,top;void init(){cou = 0;time = 1;ans = 0;top = 0;memset(head,-1,sizeof(head));memset(num,0,sizeof(num));memset(degree,0,sizeof(degree));memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));memset(vis,false,sizeof(vis));}void add_edge(int u,int v){edge[cou].to = v;edge[cou].next = head[u];head[u] = cou ++ ;}void targan(int u,int father){int i;low[u] = dfn[u] = time;time ++ ;vis[u] = true;_stack[top] = u;top ++ ;for(i=head[u];i!=-1;i=edge[i].next){int v = edge[i].to;if(!vis[v]){targan(v,u);low[u] = min(low[u],low[v]);}else{low[u] = min(low[u],dfn[v]);}}if(low[u] == dfn[u]) //找到了一个强联通分量 {ans ++ ;while(top>0 && _stack[top]!=u){top -- ;vis[_stack[top]] = true;num[_stack[top]] = ans;}}}int main(){int i,j,u,v;while(~scanf("%d%d",&n,&m)){init();for(i=0;i<m;i++){scanf("%d%d",&u,&v);add_edge(u,v);}for(i=1;i<=n;i++){if(!vis[i]){targan(i,0);}}for(i=1;i<=n;i++){for(j=head[i];j!=-1;j=edge[j].next){if(num[i]!=num[edge[j].to]){degree[num[i]] ++ ;   //记录有每个缩点的 出度 }}}int sum = 0,x,res;for(i=1;i<=ans;i++){if(!degree[i])    //统计多少个缩点出度为0的个数 {sum++;x = i;}}if(sum==1)    //出度为0的缩点只能有一个 {res = 0;for(i=1;i<=n;i++){if(num[i]==x)    //缩点为0的点 所包含点的个数 {res++;}}printf("%d\n",res);}else{printf("0\n");}}return 0;}



0 0
原创粉丝点击