zoukankan      html  css  js  c++  java
  • 【强联通图 | 强联通分量】HDU 1269 迷宫城堡 【Kosaraju或Tarjan算法】

      为了训练小希的方向感,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

    题目大意:一个有向图,有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
    ");
            else printf("No
    ");
        }
        return 0;
    }

    Tarjan算法

    上面的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
    ");
    79         else printf("No
    ");
    80     }
    81     return 0;
    82 }
    
    
    
     
  • 相关阅读:
    谁说AI看不懂视频?
    为什么说容器的崛起预示着云原生时代到来?
    小熊派开发实践丨漫谈LiteOS之传感器移植
    华为云如何赋能无人车飞驰?从这群AI热血少年谈起
    趣味科普丨一文读懂云服务器的那些事儿
    【API进阶之路】研发需求突增3倍,测试团队集体闹离职
    这个应用魔方厉害了,让软件开发者效率提升10倍
    数据安全无小事:揭秘华为云GaussDB(openGauss)全密态数据库
    数据湖探索DLI新功能:基于openLooKeng的交互式分析
    基本数据类型与表达式5 零基础入门学习Delphi06
  • 原文地址:https://www.cnblogs.com/Tangent-1231/p/11410976.html
Copyright © 2011-2022 走看看