HDOJ迷宫城堡(判断强连通 tarjan算法)

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5400    Accepted Submission(s): 2411


Problem Description
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
 
Input
输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。
 
Output
对于输入的每组数据,如果任意两个房间都是相互连接的,输出"Yes",否则输出"No"。
 
Sample Input
3 3
1 2
2 3
3 1
3 3
1 2
2 3
3 2
0 0
 
Sample Output
Yes
No
 
题意很清楚,就是判断有向图是否是强连通图,即图中任意两点是不是可以相互可达;
新学的tarjan算法,理解的还不是很透彻,参考着伪代码和大神的代码敲得;
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<vector>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 vector<int>edge[10010];//用邻接表存图;
 8 int n,m;
 9 int dfn[10010];//深度优先搜索访问次序,记录u第一次被访问的步数;
10 int low[10010];//能追溯到的最早的次序;
11 int st[10010];//tarjan中的栈;
12 int din; //索引号;
13 int num; //强连通分量个数;
14 int top; //栈中元素个数;
15 int in[10010];//是否在栈中;
16 
17 void tarjan(int u)
18 {
19     int i,j;
20     int v;
21     dfn[u] = low[u] = ++din;//为节点u设定访问序号;
22     in[u] = 1;
23     st[++top] = u;//压入栈中
24     for(i = 0; i < edge[u].size(); i++)//遍历与u相连的节点
25     {
26         v = edge[u][i];
27         if(!dfn[v])//如果节点v未被访问过
28         {
29             tarjan(v);//继续向下访问
30             low[u] = min(low[u],low[v]);
31         }
32         else if(in[v])//如果节点v已被访问过,并且已在栈中
33         {
34             low[u] = min(low[u],dfn[v]);
35         }
36     }
37 
38     if(dfn[u] == low[u])//如果节点u是强连通分量的根
39     {
40         num++;//连通分量数加1;
41         do
42         {
43             j = st[top--];
44             in[j] = 0;
45         }
46         while(j != u);//将该强连通分量中的节点退栈,
47     }
48 
49 }
50 void init()
51 {
52     int i;
53     memset(dfn,0,sizeof(dfn));
54     top = 0;
55     num = 0;
56     din = 0;
57     for(i = 1; i <= n; i++)
58     {
59         if(!dfn[i])
60         {
61             tarjan(i);
62         }
63     }
64 
65 }
66 int main()
67 {
68     int u,v;
69     while(~scanf("%d %d",&n,&m))
70     {
71         if(n == 0 && m == 0)
72             break;
73         for(int i = 0; i <= n; i++)
74             edge[i].clear();
75         while(m--)
76         {
77             scanf("%d %d",&u,&v);
78             edge[u].push_back(v);
79         }
80         init();
81         if(num == 1)
82             printf("Yes
");
83         else printf("No
");
84     }
85     return 0;
86 }
View Code

实例代码

?

tarjan(u)

{
DFN[u]=Low[u]=++Index //为节点u设定次序编号和Low初值
Stack.push(u) // 将节点u压入栈中
foreach (u, v)in E // 枚举每一条边
if(v is not visted) // 如果节点v未被访问过
tarjan(v) //继续向下找
Low[u]= min(Low[u], Low[v])
elseif(v in S) //如果节点v还在栈内
Low[u]= min(Low[u], DFN[v])
if(DFN[u]== Low[u]) //如果节点u是强连通分量的根
repeat
v = S.pop // 将v退栈,为该强连通分量中一个顶点
print v
until (u== v)
}
原文地址:https://www.cnblogs.com/LK1994/p/3330758.html