zoukankan      html  css  js  c++  java
  • SCC(强连通分量)

    1.定义:

    在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(SC---strongly connected)。

    有向图中的极大强连通子图,成为强连通分量(SCC---strongly connected components)。

    下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达,{5},{6}也分别是两个强连通分量。

    2.tarjan算法-----九野讲解

            tarjan算法的基础是DFS。我们准备两个数组Low和Dfn。Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的 Dfn值,Dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的。根据以下几条规则,经过搜索遍历该图(无需回溯)和 对栈的操作,我们就可以得到该有向图的强连通分量。

    1. 数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间。
    2. 堆栈:每搜索到一个点,将它压入栈顶。
    3. 当点p有与点p’相连时,如果此时(时间 = dfn[p]时)p’不在栈中,p的low值为两点的low值中较小的一个。
    4. 当点p有与点p’相连时,如果此时(时间 = dfn[p]时)p’在栈中,p的low值为p的low值和p’的dfn值中较小的一个。
    5. 每当搜索到一个点经过以上操作后(也就是子树已经全部遍历)的low值等于dfn值,则将它以及在它之上的元素弹出栈。这些出栈的元素组成一个强连通分量。
    6. 继续搜索(或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。
    ///其时间复杂度也是O(N+M)
    #include <stdio.h>
    #include <string.h>
    #include <vector>
    #include <stack>
    #include <iostream>
    using namespace std;
    #define N 10005            /// 题目中可能的最大点数
    stack<int>sta;            /// 存储已遍历的结点
    vector<int>gra[N];        /// 邻接表表示图
    int dfn[N];        /// 深度优先搜索访问次序
    int low[N];        /// 能追溯到的最早的次序
    int InStack[N];
    /// 检查是否在栈中(2:在栈中,1:已访问,且不在栈中,0:不在)
    vector<int> Component[N]; /// 获得强连通分量结果
    int InComponent[N];          /// 记录每个点在第几号强连通分量里
    int Index,ComponentNumber;/// 索引号,强连通分量个数
    int n, m;              /// 点数,边数
    
    void init()///清空容器,数组
    {
        memset(dfn, 0, sizeof(dfn));
        memset(low, 0, sizeof(low));
        memset(InStack, 0, sizeof(InStack));
        Index = ComponentNumber = 0;
        for (int i = 1; i <= n; ++ i)
        {
            gra[i].clear();
            Component[i].clear();
        }
        while(!sta.empty())
            sta.pop();
    }
    
    void tarjan(int u)
    {
        InStack[u] = 2;
        low[u] = dfn[u] = ++ Index;
        sta.push(u);///寻找u所在的强连通分量
        for (int i = 0; i < gra[u].size(); ++ i)
        {
            int t = gra[u][i];
            if (dfn[t] == 0)///不在的继续递归
            {
                tarjan(t);///递归到头了就
                low[u] = min(low[u], low[t]);
            }
            else if (InStack[t] == 2)///在栈里
            {
                low[u] = min(low[u], dfn[t]);
            }
        }
        if(low[u] == dfn[u])///sta出栈就是一个强连通分量的
        {
            ++ComponentNumber;///强连通分量个数
            while (!sta.empty())
            {
                int j = sta.top();
                sta.pop();
                InStack[j] = 1;///已访问但不在栈中
                Component[ComponentNumber].push_back(j);
                ///用vector存储第ComponentNumber个强连通分量
                InComponent[j]=ComponentNumber;
                ///记录每个点在第几号强连通分量里
                if (j == u)
                    break;
            }
        }
    }
    
    void input(void)
    {
        for(int i=1; i<=m; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            gra[a].push_back(b);///有向图才有强连通分量
        }
    }
    
    void solve(void)
    {
        for(int i=1; i<=n; i++)
            if(!dfn[i])
                tarjan(i);
        if(ComponentNumber > 1)
            puts("No");
        else
            puts("Yes");
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m),n+m)
        {
            init();
            input();
            solve();
            /*每一个强连通分量的具体数字
            for(int i = 1; i <= ComponentNumber; i++)
            {
                for(int j = 0; j < Component[i].size(); j++)
                    if(!j)
                        cout << Component[i][j];
                    else
                        cout <<"-->"<< Component[i][j];
                cout<<endl;
            }
            */
        }
        return 0;
    }
    HDU1269也能做模板
      1 ///时间复杂度为O(n+m)
      2 ///调用
      3 ///1、init()
      4 ///2、把图用add 存下来,注意图点标为1-n,若是[0,n-1]则给所有点++;
      5 ///3、调用tarjan_init(n); 再调用suodian();
      6 ///4、新图就是vector<int>G[];  新图点标从1-tar ;
      7 ///5、对于原图中的每个点u,都属于新图中的一个新点Belong[u];
      8 ///新图一定是森林。
      9 ///6、新图中的点u 所表示的环对应原图中的vector<int> bcc[u];
     10 ///7、旧图中u在新图中所属的点是Belong[u];
     11 #include <cstdio>
     12 #include <cstring>
     13 #include <vector>
     14 #include <stack>
     15 #include <iostream>
     16 #include <algorithm>
     17 #define repu(i, a, b) for(int i = a; i < b; i++)
     18 using namespace std;
     19 #define N 30100
     20 ///N为最大点数
     21 #define M 150100
     22 ///M为最大边数
     23 int n, m;///n m 为点数和边数
     24 
     25 struct Edge
     26 {
     27     int from, to, nex;
     28     bool sign;///是否为桥
     29 } edge[M<<1];
     30 
     31 int head[N], edgenum;
     32 
     33 void add(int u, int v) ///边的起点和终点
     34 {
     35     Edge E = {u, v, head[u], false};
     36     edge[edgenum] = E;
     37     head[u] = edgenum++;
     38 }
     39 
     40 int DFN[N], Low[N], Stack[N], top, Time;
     41 ///Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
     42 int taj;///连通分支标号,从1开始
     43 int Belong[N];///Belong[i] 表示i点属于的连通分支
     44 bool Instack[N];
     45 vector<int> bcc[N]; ///标号从1开始
     46 
     47 void tarjan(int u ,int fa)
     48 {
     49     DFN[u] = Low[u] = ++ Time ;
     50     Stack[top ++ ] = u ;
     51     Instack[u] = 1 ;
     52     for (int i = head[u] ; ~i ; i = edge[i].nex )
     53     {
     54         int v = edge[i].to ;
     55         if(DFN[v] == -1)
     56         {
     57             tarjan(v , u) ;
     58             Low[u] = min(Low[u] ,Low[v]) ;
     59             if(DFN[u] < Low[v])
     60                 edge[i].sign = 1;///为割桥
     61         }
     62         else if(Instack[v])
     63             Low[u] = min(Low[u] ,DFN[v]) ;
     64     }
     65     if(Low[u] == DFN[u])///找到一个强连通分量
     66     {
     67         int now;
     68         taj ++ ;
     69         bcc[taj].clear();
     70         do
     71         {
     72             now = Stack[-- top] ;
     73             Instack[now] = 0 ;
     74             Belong [now] = taj ;
     75             bcc[taj].push_back(now);///存储一个强连通分量
     76         }
     77         while(now != u) ;
     78     }
     79 }
     80 
     81 void tarjan_init(int all)
     82 {
     83     memset(DFN, -1, sizeof(DFN));
     84     memset(Instack, 0, sizeof(Instack));
     85     memset(Low, 0, sizeof(Low));
     86     top = Time = taj = 0;
     87     for(int i=1; i<=all; i++)
     88         if(DFN[i] == -1 )
     89             tarjan(i, i); ///注意开始点标!!!
     90 }
     91 
     92 vector<int>G[N];
     93 int du[N];
     94 void suodian()
     95 {
     96     memset(du, 0, sizeof(du));
     97     for(int i = 1; i <= taj; i++)
     98         G[i].clear();
     99     for(int i = 0; i < edgenum; i++)
    100     {
    101         int u = Belong[edge[i].from], v = Belong[edge[i].to];
    102         if(u!=v)
    103             G[u].push_back(v), du[v]++;
    104     }
    105 }
    106 
    107 void init()
    108 {
    109     memset(head, -1, sizeof(head));
    110     edgenum=0;
    111 }
    112 
    113 int main()
    114 {
    115     while(scanf("%d%d",&n,&m) && (n + m))
    116     {
    117         init();
    118         repu(i,0,m)
    119         {
    120             int a,b;
    121             scanf("%d%d",&a,&b);
    122             add(a,b);
    123         }
    124         tarjan_init(n);
    125 //        repu(i,1,taj + 1)///输出每一个强连通分量
    126 //        {
    127 //            for(int j = 0; j < bcc[i].size(); j++)
    128 //                cout<<bcc[i][j]<<" ";
    129 //            cout<<endl;
    130 //        }
    131         if(taj > 1)
    132             printf("No
    ");
    133         else
    134             printf("Yes
    ");
    135     }
    136     return 0;
    137 }
    九野模板--HDU1269

    参考链接http://blog.csdn.net/xinghongduo/article/details/6195337

  • 相关阅读:
    [require-js]向下滑动ajax加载的javascript实现
    Date的ToString方法
    GMAT语法总结
    流程控制语句:if、if else、if else if、嵌套if
    Random类
    Scanner类
    java运算符
    java数据类型转换
    mysql查询语句
    mysql常用语句
  • 原文地址:https://www.cnblogs.com/ACMERY/p/4679807.html
Copyright © 2011-2022 走看看