【強聯通圖 | 強聯通份量】HDU 1269 迷宮城堡 【Kosaraju或Tarjan算法】

  爲了訓練小希的方向感,Gardon創建了一座大城堡,裏面有N個房間(N<=10000)和M條通道(M<=100000),每一個通道都是單向的,就是說若稱某通道連通了A房間和B房間,只說明能夠經過這個通道由A房間到達B房間,但並不說明經過它能夠由B房間到達A房間。Gardon須要請你寫個程序確認一下是否任意兩個房間都是相互連通的,即:對於任意的i和j,至少存在一條路徑能夠從房間i到房間j,也存在一條路徑能夠從房間j到房間i。 

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 }
相關文章
相關標籤/搜索