Inputios
輸入包含多組數據,輸入的第一行有兩個數:N和M,接下來的M行每行有兩個數a和b,表示了一條通道能夠從A房間來到B房間。文件最後以兩個0結束。
Output算法
對於輸入的每組數據,若是任意兩個房間都是相互鏈接的,輸出"Yes",不然輸出"No"。
Sample Inputspa
3 3 1 2 2 3 3 1 3 3 1 2 2 3 3 2 0 0
Sample Outputcode
Yes No
題目大意:一個有向圖,有n個點和m條邊。判斷整個圖是否強連通,若是是,輸出Yes,不然輸出No。
題目能夠用Kosaraju算法和Tarjan算法。
詳解來自於:《算法競賽 入門到進階》
Kosaraju算法:
Kosaraju算法用到了「反圖」的技術,基於下面兩個原理:
(1)一個有向圖G,把G全部的邊反向,創建反圖rG,反圖rG不會改變原圖G的強連通性。也就是說,圖G的SCC數量與rG的SCC(強聯通份量)數量相同。
(2)對原圖G和反圖rG各作一次DFS,能夠肯定SCC數量。
代碼:
#pragma comment(linker, "/STACK:1024000000,1024000000") #pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #include<set> #include<cmath> #include<string> #include<map> #include<vector> #include<ctime> #include<stack> using namespace std; #define mm(a,b) memset(a,b,sizeof(a)) typedef long long ll; const long long mod = 1e9+7; const int maxn = 1e4+10; const int inf = 0x3f3f3f3f; vector<int>G[maxn],rG[maxn]; vector<int>S;//存第一次dfs1的結果:標記點的前後順序 int vis[maxn],sccno[maxn],cnt;//cnt爲連通份量的個數 void dfs1(int u) { if(vis[u]) return; vis[u]=1; for(int i=0;i<G[u].size();i++) dfs1(G[u][i]); S.push_back(u);//標記點的前後順序,標記大的放在S的後面 } void dfs2(int u) { if(sccno[u]) return; sccno[u]=cnt; for(int i=0;i<rG[u].size();i++) dfs2(rG[u][i]); } void Kosaraju(int n) { cnt=0; S.clear(); mm(sccno,0); mm(vis,0); for(int i=1;i<=n;i++) dfs1(i); //點的編號:1~n遞歸全部點 for(int i=n-1;i>=0;i--) if(!sccno[S[i]]) { cnt++; dfs2(S[i]); } } int main() { int n,m,u,v; while(scanf("%d %d",&n,&m),n||m) { for(int i=0;i<n;i++) { G[i].clear(); rG[i].clear(); } for(int i=0;i<m;i++) { scanf("%d %d",&u,&v); G[u].push_back(v); rG[v].push_back(u); } Kosaraju(n); if(cnt==1) printf("Yes\n"); else printf("No\n"); } return 0; }
Tarjan算法blog
上面的Kosaraju算法,其作法是從圖中一個個地把SCC「挖」出來。Tarjan算法能在DFS中把全部點都按SCC分開。遞歸
1 #pragma comment(linker, "/STACK:1024000000,1024000000") 2 #pragma GCC optimize(2) 3 #include<iostream> 4 #include<algorithm> 5 #include<cstdio> 6 #include<cstring> 7 #include<queue> 8 #include<set> 9 #include<cmath> 10 #include<string> 11 #include<map> 12 #include<vector> 13 #include<ctime> 14 #include<stack> 15 using namespace std; 16 #define mm(a,b) memset(a,b,sizeof(a)) 17 typedef long long ll; 18 const long long mod = 1e9+7; 19 const int maxn = 1e4+10; 20 const int inf = 0x3f3f3f3f; 21 int cnt; //強連通份量的個數 22 int low[maxn],num[maxn],dfn; 23 int sccno[maxn]; 24 stack<int>st; 25 vector<int>G[maxn]; 26 27 void dfs(int u) 28 { 29 st.push(u); 30 low[u]=num[u]=++dfn; 31 for(int i=0;i<G[u].size();i++) 32 { 33 int v=G[u][i]; 34 if(!num[v]) //未訪問過的點,繼續DFS 35 { 36 dfs(v); //DFS的最底層,是最後一個SCC 37 low[u]=min(low[v],low[u]); 38 } 39 else if(!sccno[v]) //處理回退邊 40 low[u]=min(low[u],num[v]); 41 } 42 if(low[u]==num[u]) //棧底的點是SCC的祖先,它的low=num 43 { 44 cnt++; 45 while(1) 46 { 47 int v=st.top(); //v彈出棧 48 st.pop(); 49 sccno[v]=cnt; 50 if(u==v) break; //棧底的點是SCC的祖先 51 } 52 } 53 } 54 55 void Tarjan(int n) 56 { 57 cnt=dfn=0; 58 mm(sccno,0); 59 mm(num,0); 60 mm(low,0); 61 for(int i=1;i<=n;i++) 62 if(!num[i]) 63 dfs(i); 64 } 65 66 int main() 67 { 68 int n,m,u,v; 69 while(scanf("%d %d",&n,&m),n||m) 70 { 71 for(int i=1;i<=n;i++) G[i].clear(); 72 for(int i=0;i<m;i++) 73 { 74 scanf("%d %d",&u,&v); 75 G[u].push_back(v); 76 } 77 Tarjan(n); 78 if(cnt==1) printf("Yes\n"); 79 else printf("No\n"); 80 } 81 return 0; 82 }