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 }
实例代码
?
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)
}