問題來源:hdu-1269
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
源代碼:
#include<iostream>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
const int MAX = 10002;
const int INF = 1<<30;
vector <int> G[MAX]; //vector構圖
stack <int> S; //利用數據結構棧實現
int N , M , now , sum;
int dfn[MAX] , low[MAX] , mark[MAX];
int Min( int a , int b ){ return a <= b ? a : b; }
void Tarjin( int u );
int main( ){
int a , b;
while( cin>>N>>M && N+M ){
sum = now = 0;
memset( dfn , 0 , sizeof( dfn ) );
memset( low , 0 , sizeof( low ) );
memset( mark , 0 , sizeof( mark ) );
for( int i=0 ; i<MAX ; i++ ) G[i].clear( );
while( !S.empty() ) S.pop( );
while( M-- ){
cin>>a>>b;
G[a].push_back( b ); //建立單向的圖
}
for( int i=1 ; i<=N ; i++ ){ //每個點都可能是新的強連通分量,需要統統遍歷
if( !mark[i] )
Tarjin( i );
}
if( sum==1 ) //強連通分量的數量只爲1,即整個圖是一個強連通圖
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
void Tarjin( int u ){
int v;
dfn[u] = low[u] = ++now; //時間戳和最小標號
mark[u] = 1; //標記掃過
S.push( u ); //入棧
for( int i=0 ; i<G[u].size() ; i++ ){ //對於該點爲弧尾的每一條邊
v = G[u][i];
if( !dfn[v] ){ //遞歸進行Tarjin
Tarjin( v );
low[u] = Min( low[u] , low[v] ); //如果v沒有搜過,更新最小標號
}
else if( mark[v] )
low[u] = Min( low[u] , dfn[v] ); //搜過了該點,已經入棧,則更新最小標號
}
if( dfn[u]==low[u] ){ //找到強連通分量
sum++; //數量累加
while( !S.empty() ){
int tmp = S.top( );
S.pop( );
if( tmp==u ) //找到相等的就break
break;
}
}
}
代碼分析:Tarjin算法是基於dfs來實現的算法,dfn記錄入棧的次序(時間戳,入棧之後並不會改變),low記錄它能直接或間接達到的最小時間戳頂點,所以當dfn[i]==low[i]時,請棧中取出相應元素即可找出一個強連通分量