參考html
Kosarajuc++
http://www.javashuo.com/article/p-cmjtpfng-cx.html算法
tarjanide
https://blog.csdn.net/qq_34374664/article/details/77488976spa
思路.net
求迴路也就是求圖的強連通份量code
共有兩種算法:Kosaraju與tarjan,其中Kosaraju算法較易理解但須要兩次dfs,tarjan算法較難理解但只需dfs一次。htm
兩種算法的詳解見參考。blog
概述:ci
Kosaraju
一、對圖的反向圖進行一次逆後序dfs遍歷
二、按照逆後序遍歷獲得的棧中點的出棧順序對原圖進行dfs遍歷,每一次遍歷訪問到的點就屬於一個強連通份量。
tarjan
1 /* 2 dfn爲訪問序號(時間戳) 3 low爲當前節點及其子樹中結點最小的訪問序號,相似並查集 4 */ 5 void tarjan(int u){ 6 7 DFN[u]=Low[u]=++Index // 爲節點u設定次序編號和Low初值 8 9 Stack.push(u) // 將節點u壓入棧中 10 11 for each (u, v) in E // 枚舉每一條邊 12 { 13 if (v is not visted) // 若是節點v未被訪問過 14 15 { 16 tarjan(v) // 繼續向下找 17 Low[u] = min(Low[u], Low[v]) 18 } 19 20 else if (v in S) // 若是節點u還在棧內 21 22 { Low[u] = min(Low[u], DFN[v]) } 23 } 24 25 if (DFN[u] == Low[u]) // 若是節點u是強連通份量的根 26 { 27 repeat 28 29 v = S.pop // 將v退棧,爲該強連通份量中一個頂點 30 31 print v 32 33 until (u== v) 34 } 35 36 }
實現
Kosaraju
1 //Kosaraju 2 #include<bits/stdc++.h> 3 4 using namespace std; 5 6 #define MAXN 10005 7 8 vector<int> Map1[MAXN]; 9 vector<int> Map2[MAXN]; 10 stack<int> sta; 11 int vis[MAXN]; 12 long long int ans=0,num=0; 13 int n,m; 14 15 int size,v; 16 void dfs2(int s){ 17 18 for(int i=0;i<Map2[s].size();i++){ 19 v=Map2[s][i]; 20 if(vis[v]==0){ 21 vis[v]=1; 22 dfs2(v); 23 } 24 } 25 sta.push(s); 26 } 27 28 void dfs1(int s){ 29 for(int i=0;i<Map1[s].size();i++){ 30 v=Map1[s][i]; 31 if(vis[v]==0){ 32 //cout<<v<<' '; 33 vis[v]=1; 34 num++; 35 dfs1(v); 36 } 37 } 38 } 39 40 long long int C(int x){ 41 if(x<=1){ 42 return 0; 43 } 44 else{ 45 long long int a=1; 46 for(int i=x;i>x-2;i--){ 47 a*=i; 48 } 49 return a/2; 50 } 51 } 52 53 int main(){ 54 cin>>n>>m; 55 int a,b; 56 for(int i=0;i<m;i++){ 57 cin>>a>>b; 58 Map1[a].push_back(b); 59 Map2[b].push_back(a); 60 } 61 62 memset(vis,0,sizeof(vis)); 63 for(int i=0;i<=n;i++){ 64 if(vis[i]==0){ 65 vis[i]=1; 66 dfs2(i); 67 } 68 } 69 70 memset(vis,0,sizeof(vis)); 71 int v; 72 while(!sta.empty()){ 73 v=sta.top(); 74 sta.pop(); 75 76 if(vis[v]==0){ 77 //cout<<v<<' '; 78 vis[v]=1; 79 num=1; 80 dfs1(v); 81 //cout<<num<<endl; 82 ans+=C(num); 83 } 84 } 85 86 cout<<ans; 87 88 return 0; 89 }
tarjan
1 //tarjan 2 3 #include<bits/stdc++.h> 4 5 using namespace std; 6 7 #define MAXN 10005 8 9 vector<int> Map[MAXN]; 10 stack<int> sta; 11 int vis[MAXN]; 12 int dfn[MAXN]; 13 int low[MAXN]; 14 int insta[MAXN]; 15 long long int num=0,ans=0; 16 int n,m,index; 17 18 long long int C(int x){ 19 if(x<=1){ 20 return 0; 21 } 22 else{ 23 long long int a=1; 24 for(int i=x;i>x-2;i--){ 25 a*=i; 26 } 27 return a/2; 28 } 29 } 30 31 void tarjan(int u) 32 { 33 index++; 34 dfn[u]=low[u]=index; 35 vis[u]=1; 36 sta.push(u); 37 insta[u]=1; 38 int size=Map[u].size(); 39 for(int i=0;i<size;i++){ 40 int v=Map[u][i]; 41 if(vis[v]==0){ 42 tarjan(v); 43 low[u]=min(low[u],low[v]); 44 } 45 else if(insta[v]==1){ 46 low[u]=min(low[u],dfn[v]); 47 } 48 } 49 num=0; 50 if(dfn[u]==low[u]){ 51 int v; 52 do{ 53 v=sta.top(); 54 //cout<<v<<' '; 55 sta.pop(); 56 insta[v]=0; 57 num++; 58 }while(v!=u); 59 //cout<<endl; 60 } 61 ans+=C(num); 62 } 63 64 int main(){ 65 cin>>n>>m; 66 int a,b; 67 for(int i=0;i<m;i++){ 68 cin>>a>>b; 69 Map[a].push_back(b); 70 } 71 memset(vis,0,sizeof(vis)); 72 memset(insta,0,sizeof(insta)); 73 index=0; 74 for(int i=1;i<=n;i++){ 75 if(vis[i]==0){ 76 tarjan(i); 77 } 78 } 79 cout<<ans; 80 81 return 0; 82 }
題目
問題描述
某國有
n個城市,爲了使得城市間的交通更便利,該國國王打算在城市之間修一些高速公路,因爲經費限制,國王打算第一階段先在部分城市之間修一些單向的高速公路。
如今,大臣們幫國王擬了一個修高速公路的計劃。看了計劃後,國王發現,有些城市之間能夠經過高速公路直接(不通過其餘城市)或間接(通過一個或多個其餘城市)到達,而有的卻不能。若是城市A能夠經過高速公路到達城市B,並且城市B也能夠經過高速公路到達城市A,則這兩個城市被稱爲便利城市對。 國王想知道,在大臣們給他的計劃中,有多少個便利城市對。
輸入格式
輸入的第一行包含兩個整數
n,
m,分別表示城市和單向高速公路的數量。
接下來 m行,每行兩個整數 a, b,表示城市 a有一條單向的高速公路連向城市 b。
輸出格式
輸出一行,包含一個整數,表示便利城市對的數量。
樣例輸入
5 5
1 2 2 3 3 4 4 2 3 5
樣例輸出
3
樣例說明
城市間的鏈接如圖所示。有3個便利城市對,它們分別是(2, 3), (2, 4), (3, 4),請注意(2, 3)和(3, 2)當作同一個便利城市對。
評測用例規模與約定
前30%的評測用例知足1 ≤
n ≤ 100, 1 ≤
m ≤ 1000;
前60%的評測用例知足1 ≤ n ≤ 1000, 1 ≤ m ≤ 10000; 全部評測用例知足1 ≤ n ≤ 10000, 1 ≤ m ≤ 100000。 |