HDU1269-迷宫城堡-tarjan算法
http://acm.hdu.edu.cn/showproblem.php?pid=1269
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void tarjan(int x) 2 { 3 int i, a; 4 low[x] = dfn[x] = index; // 刚搜到一个节点时low = dfn 5 index++; 6 stack[++head] = x; // 将该节点入栈 7 instack[x] = 1; // 将入栈标记设置为1 8 for(i = 1; i <= n; i++) // 遍历入栈节点的边 9 { 10 if(!map[x][i]) // 如果两点之间没有边 11 continue; // 不用管它 12 if(dfn[i] == -1) // 如果新搜索到的节点是从未被搜索过 13 { 14 tarjan(i); // 那自然就得搜索这个节点 15 low[x] = min(low[x], low[i]); // 回溯的时候改变当前节点的low值 16 } 17 else if(instack[i]) // 如果新搜索到的节点已经被搜索过而且现在在栈中 18 { 19 low[x] = min(low[x], dfn[i]); // 更新当前节点的low值,这里的意思是两个节点之间有一条可达边,而前面 20 } // 而前面节点已经在栈中,那么后面的节点就可能和前面的节点在一个联通分量中 21 } 22 23 if(low[x] == dfn[x]) // 最终退回来的时候 low == dfn , 没有节点能将根节点更新,那 24 { // low == dfn 的节点必然就是根节点 25 int temp; 26 while(1) // 一直出栈到此节点, 这些元素是一个强联通分量 27 { 28 temp = stack[head--]; // 弹出栈元素 29 belong[temp] = cnt; // 为了方便计算,将强联通分量进行标记 30 instack[temp] = 0; // 将栈内标记置为0 31 if(temp == x) // 一直弹到x出现为止 32 break; 33 } 34 cnt++; 35 } 36 } 37 38 39 void solve() 40 { 41 int i, j; 42 int t1, t2; 43 while(scanf("%d", &n) != EOF) // 44 { 45 init(); // 初始化 46 for(i = 1; i <= n; i++) // 47 if(dfn[i] == -1) // 如果某点没被访问过,则对其进行tarjan 48 tarjan(i); // tarjan的成果是得到了一个belong数组,记录每个节点分别属于哪个强联通分量 49 for(i = 1; i <= n; i++) // 遍历每条边,找到缩点之后的边 50 { 51 for(j = 1;j <= n; j++) 52 { 53 if(map[i][j] && belong[i] != belong[j]) // 两点之间有边,但不是属于一个强联通分量的边 54 { 55 out[belong[i]]++; // 缩点后的点入度+1 56 in[belong[j]]++;// 缩点后的点出度+1 57 } 58 } 59 } 60 61 t1 = 0, t2 = 0; 62 63 for(i = 1; i < cnt; i++) 64 { 65 if(in[i] == 0) 66 t1++; 67 if(out[i] == 0) 68 t2++; 69 } 70 if(cnt == 2) 71 printf("1\n0\n"); 72 else 73 printf("%d\n%d\n", t1, max(t1, t2)); 74 } 75 } 76
#include<iostream> #include<vector> using namespace std; #define Max 10010 //题目中可能的最大点数 int STACK[Max],top=0; //Tarjan 算法中的栈 bool InStack[Max]; //检查是否在栈中 int DFN[Max]; //深度优先搜索访问次序 int Low[Max]; //能追溯到的最早的次序 int ComponentNumber=0; //有向图强连通分量个数 int Index=0; //索引号 vector <int> Edge[Max]; //邻接表表示 vector <int> Component[Max]; //获得强连通分量结果 int InComponent[Max]; //记录每个点在第几号强连通分量里 int ComponentDegree[Max]; //记录每个强连通分量的度 void Tarjan(int i) { int j; DFN[i]=Low[i]=Index++; InStack[i]=true; STACK[++top]=i; for (int e=0; e<Edge[i].size(); e++) { j=Edge[i][e]; if (DFN[j]==-1) { Tarjan(j); Low[i]=min(Low[i],Low[j]); } else if (InStack[j]) Low[i]=min(Low[i],DFN[j]); } if (DFN[i]==Low[i]) { ComponentNumber++; do { j=STACK[top--]; InStack[j]=false; Component[ComponentNumber].push_back(j); InComponent[j]=ComponentNumber; } while (j!=i); //cout<<ComponentNumber<<endl; } } void solve(int N) //N是此图中点的个数,注意是0-indexed! { memset(STACK,-1,sizeof(STACK)); memset(InStack,0,sizeof(InStack)); memset(DFN,-1,sizeof(DFN)); memset(Low,-1,sizeof(Low)); top=0; Index=0; ComponentNumber=0; for(int i=1;i<=N;i++) if(DFN[i]==-1) Tarjan(i); } int main() { int N, M; int a, b; while(cin>>N>>M, N||M) { int i; for(i=0; i<=N+1; i++) { Component[i].clear(); Edge[i].clear(); } for(i=1; i<=M; i++) { scanf("%d%d", &a, &b); Edge[a].push_back(b); } solve(N); if(ComponentNumber == 1) cout<<"Yes\n"; else cout<<"No\n"; } return 0; }