• (转)求有向图的强连通分量个数(kosaraju算法)


    有向图的连通分量的求解思路

    kosaraju算法

    逛了很多博客,感觉都很难懂,终于找到一篇能看懂的,摘要记录一下

    原博客https://www.cnblogs.com/nullzx/p/6437926.html

    关于连通分量是什么自行百度,这里主要说明连通分量的求解方法

    基本思路:第一次DFS得出顶点的顺序,根据顶点顺序进行第二次DFS,也就是逆后序遍历(手动模拟一下堆栈就知道第二次DFS的过程就能得出答案)。

    为什么要两次DFS?

    如果从连通分量A中任意一个定点DFS,得不到正确结果。应该按照被指向的强连通分量的定点排在前面的顺序进行DFS。上图按照B3,B4,B5,A0,A1,A2的顺序DFS。实际中我们只要保证被指向的强连通分量的至少一个顶点排在指向这个连通分量的所有顶点前面即可,比如B3,A0,A1,A2,B4,B5;B3排在强连通分量A所有定点的前面。

    如何得到满足要求的顶点顺序:对原图取反,从反向图的任意节点开始进行DFS的逆后序遍历

    DFS的逆后序遍历指:如果当前顶点没被访问,先遍历完与当前顶点相连的且未被访问的所有其他顶点,然后将当前顶点加入栈,最后从栈顶到栈底的顺序是我们需要的顶点顺序。

    其实它是利用了有向图的方向性:例如在上图中,强连通分量A和B不管正反图都能自己跑一圈,但是从A到B就只能从A2跑到B3,不可能从B3跑到A2,所以将图取反(注意图取反,不能从A2跑到B3,顶点顺序被记录,按顶点顺序一个个弹出遍历,之前遍历过的点就不用再遍历),做成反向图,再逆后序遍历,A0在栈顶,遍历一圈,不能从A2跑到B3,就得到一个连通分量(如下图)

    接下来原博主的代码看不懂,还是百度百科kosaraju算法里面的代码好

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cmath>
     4 #include <cstdio>
     5 #include <stack>
     6 #include <algorithm>
     7 using namespace std;
     8 #define INF 999999999
     9 #define MAXN 551
    10 #define MOD 1000000009
    11 int n;
    12 int mp[MAXN][MAXN], nmp[MAXN][MAXN], vis[MAXN];
    13 stack<int> s;
    14 void dfs_1(int v)
    15 {
    16 //    cout << v <<endl;
    17     vis[v] = 1;
    18     for(int i = 1; i <= n; ++i)
    19         if(!vis[i] && mp[v][i])
    20         dfs_1(i);
    21     s.push(v);
    22 }
    23 void dfs_2(int v)
    24 {
    25     vis[v] = 1;
    26     for(int i = 1; i <= n; ++i)
    27         if(!vis[i] && nmp[v][i])
    28         dfs_2(i);
    29 }
    30 int kosaraju()
    31 {
    32     while(!s.empty())
    33         s.pop();
    34     memset(vis, 0, sizeof(vis));
    35     
    36     for(int i = 1; i <= n; ++i)
    37         if(!vis[i])
    38         dfs_1(i);
    39         
    40     int ans=0;
    41     memset(vis, 0, sizeof(vis));
    42     
    43     while(!s.empty())
    44     {
    45         int v = s.top();
    46         s.pop();
    47         if(!vis[v])
    48         {
    49 //            cout << v <<endl;
    50             ans++;
    51             dfs_2(v);
    52         }
    53     }
    54     return ans;
    55 }
    56 int main()
    57 {
    58     int m, a, b;
    59     //n个点,m条边
    60     cin >> n >>m;
    61     memset(mp, 0, sizeof(mp));
    62     memset(nmp, 0, sizeof(nmp));
    63     for(int i = 0; i < m; ++i)
    64     {
    65         cin >> a >>b;
    66         mp[a][b] = 1;
    67         nmp[b][a] = 1;
    68     }
    69     cout << kosaraju() <<endl;
    70     return 0;
    71 }
    72 /*
    73 5 5
    74 1 2
    75 2 1
    76 2 3
    77 3 4
    78 4 1
    79 样例输出:
    80 2
    81 */
    82  

    tarjan算法

    这个算法网上很容易找到详解,其实认真看一遍百度百科也就懂得七七八八了

    再附详解地址,这位博主也是转别人的,但是那个别人的博客打不开了

    https://blog.csdn.net/qq_34374664/article/details/77488976

    这个算法的核心思想:将连通分量的各个点用一个点表示。

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cmath>
     4 #include <cstdio>
     5 #include <stack>
     6 #include <algorithm>
     7 
     8 using namespace std;
     9 
    10 #define INF 0x3f3f3f3f
    11 #define MAXN 551
    12 
    13 int DFN[MAXN], Low[MAXN];
    14 int vis[MAXN], sta_ck[MAXN];
    15 int Index, cnt, tot;
    16 int n;
    17 
    18 struct Node
    19 {
    20     int to;
    21     int next;
    22 }node[MAXN];
    23 int head[MAXN];
    24 
    25 //用head作为头指针指向下一个结点下标,让next指向head指向的下标后,head指向该结点
    26 void add(int x, int y)
    27 {
    28     node[++cnt].next = head[x];
    29     node[cnt].to = y;
    30     head[x] = cnt;
    31 }
    32 
    33 void tarjan(int x)
    34 {
    35     DFN[x] = Low[x] = ++tot;
    36     sta_ck[++Index] = x;
    37     vis[x] = 1;
    38     for(int i = head[x]; i != -1; i = node[i].next)
    39     {
    40         if(!DFN[node[i].to])
    41         {
    42             tarjan(node[i].to);
    43             Low[x] = min(Low[x], Low[node[i].to]);
    44         }
    45         else if(vis[node[i].to])
    46             Low[x] = min(Low[x], DFN[node[i].to]);
    47     }
    48     if(Low[x] == DFN[x])
    49     {
    50         do
    51         {
    52             cout << sta_ck[Index] <<" ";
    53             vis[sta_ck[Index]]=0;
    54             Index--;
    55         }while(x != sta_ck[Index + 1]);
    56         cout << endl;
    57     }
    58 
    59 }
    60 
    61 int main()
    62 {
    63     memset(head, -1, sizeof(head));
    64     memset(DFN, 0, sizeof(DFN));
    65     memset(Low, 0, sizeof(Low));
    66     int n, m;
    67     cin >>n >>m;
    68     int x, y;
    69     for(int i = 1; i <= m; ++i)
    70     {
    71         cin >>x >>y;
    72         add(x, y);
    73     }
    74     for(int i = 1; i <= n; ++i)
    75         if(!DFN[i])
    76         tarjan(i);
    77     return 0;
    78 }
    现在所有的不幸都是以前不努力造成的。。。
  • 相关阅读:
    POJ2395 Out of Hay
    POJ2421 Constructing Roads
    POJ2031 Building a Space Station
    POJ1751 Highways
    [NOIP2007] 提高组 洛谷P1099 树网的核
    [NOIP2007] 提高组 洛谷P1005 矩阵取数游戏
    [NOIP2007] 提高组 洛谷P1097 统计数字
    [NOIP2007] 提高组 洛谷P1098 字符串的展开
    [NOIP2007] 普及组
    洛谷 通天系列 P1760 P1757 P1759
  • 原文地址:https://www.cnblogs.com/shuizhidao/p/8747688.html
Copyright © 2020-2023  润新知