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

    有向图的强连通分量

    在有向图中,u可达v不一定意味v可达到u,相互可达的节点则属于同一个强连通分量。

    某节点的传递闭包为该节点所处的强连通分量和它全部后代所处的强连通分量的节点。

    若有向图的全部节点同属于一个强连通分量。则称该有向图为强连通图。

    在有向图中,若某子图中的任一对节点都互为可达。则该子图称为有向图的强连通分量。

    计算有向图中强连通分量的方法例如以下:将有向图G中每条边的方向取反。得到图G的一个转置GT,G和GT中的强连通分量同样。将每一个强连通分量缩成一个节点,就能够得到一个无环有向图Gscc。

                                          



       



                                                                          

    1、Procedure Kosaraju(G);
    {调用dfs(G)计算出每一个节点的f[u];
    计算图G的转置GT;
    调用dfs(GT),在主循环中依照f[u]递减的顺序依次对每一个未訪问点运行dfs过程。则得到的每棵dfs树恰好相应于一个强连通分量;
    }






                               

    V

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    d

    1

    17

    2

    3

    4

    5

    7

    8

    9

    19

    21

    23

    20

    f

    16

    18

    15

    14

    13

    6

    12

    11

    10

    26

    22

    24

    25








    f

    26

    25

    24

    22

    18

    16

    15

    14

    13

    12

    11

    10

    6

    v

    9

    12

    11

    10

    1

    0

    2

    3

    4

    6

    7

    8

    5

    0

    0

    0

    0

    1

    2

    2

    2

    2

    2

    3

    3

    2



    模板:
    const maxn=100;
    var        //map[x,i] 记录与点x邻接的第i个点的编号   map[x,0]记录和x邻接的点的个数
      map,map1:array[1..maxn,0..maxn]of integer;  
      visit:array[1..maxn]of boolean;   //记录该点是否被遍历过
      list:array[1..maxn]of integer;      //记录n个点的遍历次序
      n,m,pos,scc:integer;                  //pos记录进入list数组的点的个数  scc记录强连通分量的个数 

    procedure init;
    var i,x,y:integer;
    begin
      readln(n,m);
      for i:=1 to m do
      begin
        readln(x,y);
        inc(map[x,0]);
        map[x,map[x,0]]:=y;   
        inc(map1[y,0]);
        map1[y,map1[y,0]]:=x; 
    end;
    end;

    procedure dfs(p:integer);
    var i,j,k:integer;
    begin
      visit[p]:=true; k:=map[p,0];
      for i:=1 to k do
      begin
        j:=map[p,i];
        if not visit[j] then dfs(j);
      end;
      inc(pos);  list[pos]:=p;
    end;

    procedure dfs1(p:integer);
    var i,j,k:integer;
    begin
      visit[p]:=true;
      k:=map1[p,0];
      for i:=1 to k do
      begin
        j:=map1[p,i];
        if not visit[j] then dfs1(j);
      end;
    end;

    procedure kosaraju;
    var i,j,k:integer;
    begin
    fillchar(visit,sizeof(visit),false);
      for i:=1 to n do if not visit[i] then dfs(i); 
      fillchar(visit,sizeof(visit),false);  scc:=0;
      for i:=pos downto 1 do  //每深搜完一次,表示找完一个强连通图。添加scc
        if not visit[list[i]] then begin dfs1(list[i]); inc(scc); end;
    end;

    begin
      init;
      pos:=0;
      kosaraju;
      writeln(scc);
    end.


    2、Tarjan算法

    Tarjan算法是基于对图深度优先搜索的算法。每一个强连通分量为搜索树中的一棵子树。

    搜索时,把当前搜索树中未处理的节点增加一个堆栈,回溯时能够推断栈顶到栈中的节点是否为一个强连通分量。




    算法流程演示:

    1.从节点1開始DFS,把遍历到的节点增加栈中。

    搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止。{6}为一个强连通分量。


    2.返回节点5。发现DFN[5]=LOW[5]。退栈后{5}为一个强连通分量。


    3.返回节点3,继续搜索到节点4。把4增加堆栈。发现节点4向节点1有后向边。节点1还在栈中,所以LOW[4]=1。

    节点6已经出栈,(4,6)是横叉边,返回3。(3,4)为树枝边,所以LOW[3]=LOW[4]=1。


    4.继续回到节点1,最后訪问节点2。訪问边(2,4)。4还在栈中,所以LOW[2]=DFN[4]=5。返回1后,发现DFN[1]=LOW[1],把栈中节点所有取出,组成一个连通分量{1,3,4,2}。


    至此,算法结束。经过该算法,求出了图中所有的三个强连通分量{1,3,4,2},{5},{6}。
    能够发现,执行Tarjan算法的过程中。每一个顶点都被訪问了一次,且仅仅进出了一次堆栈,每条边也仅仅被訪问了一次,所以该算法的时间复杂度为O(N+M)。

    模板:

    var
      a:array [1..1000,1..1000] of longint;
      low,dfn,c:array [1..1000] of longint;
      v,f,ff:array [1..1000] of boolean;
      i,j,m,n,x,y,d,ans:longint;


    function min(x,y:longint):longint;
    begin
      if x>y then
      exit(y);
      exit(x);
    end;


    procedure tarjan(x:longint);
    var
      i:longint;
    begin
      inc(d);
      low[x]:=d;
      dfn[x]:=d;
      f[x]:=true;
      for i:=1 to c[x] do
      begin
        if not v[a[x,i]] then
        begin
          v[a[x,i]]:=true;
          tarjan(a[x,i]);
          low[x]:=min(low[x],low[a[x,i]]);
        end else
        begin
          if f[a[x,i]] then
          low[x]:=min(low[x],dfn[a[x,i]]);
        end;
      end;
      if dfn[x]=low[x] then
      inc(ans);
    end;


    begin
      readln(n);
      for i:=1 to n do
      begin
        read(x);
        while x<>0 do
        begin
          inc(c[i]);
          a[i,c[i]]:=x;
          read(x);
        end;
      end;
      for i:=1 to n do
      if not v[i] then
      begin
        v[i]:=true;
        tarjan(i);
      end;
      writeln(ans);
    end.


    有几道习题:

    1.http://blog.csdn.net/boyxiejunboy/article/details/46891399

    2.http://blog.csdn.net/boyxiejunboy/article/details/46891417

    3.http://blog.csdn.net/boyxiejunboy/article/details/46891815


    具体详见:http://download.csdn.net/detail/boyxiejunboy/8908487(更加具体哦)

  • 相关阅读:
    spring core源码解读之ASM4用户手册翻译之一asm简介
    nginx启动,重启,关闭命令
    linux LVM分区查看dm设备
    jdbc 对sqlite的基本操作
    linux配置多个ip
    细说Linux下的虚拟主机那些事儿
    打造字符界面的多媒体Linux系统
    linux计划crontab
    因修改/etc/ssh权限导致的ssh不能连接异常解决方法
    Linux修改主机名
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/6703046.html
Copyright © 2011-2022 走看看