• 半连通分量--Tarjan/Kosaraju算法


          一个有向图称为半连通(Semi-Connected),满足:对于图中任两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。 若满足,则称G’是G的一个导出子图。

          若G’是G的导出子图,且G’半连通,则称G’为G的半连通子图。若G’是G所有半连通子图中包含节点数最多的,则称G’是G的最大半连通子图。

    判断一个图是不是半连通图

        求解:<1>Kosarsju算法: [1] 新图DFS    [2] 方法2

                <2>Tarjan算法:[1] 新图DFS

    【1】新图DFS

     1 void init_judge(void)
     2 {
     3     for(int i=1;i<=num_scc;i++)
     4     {
     5         vis_scc[i]=0;
     6     }
     7     root=0;
     8     is_halfSCC=0;
     9 }
    10 void judge_halfSCC(int u,int depth)
    11 {
    12     ENode *ptr=(ENode *)malloc(sizeof(ENode));
    13     int son;
    14 
    15     if(depth == num_scc)
    16         is_halfSCC = 1;
    17     else
    18     {
    19         ptr=rebuild_ALG->vlist[u].firstedge;
    20         while(ptr!=NULL)
    21         {
    22             son=ptr->key;
    23             if(!vis_scc[son])
    24             {
    25                 vis_scc[son]=1;
    26                 judge_halfSCC(son,depth+1);
    27                 vis_scc[son]=0;  //回溯时要用到
    28             }
    29             ptr=ptr->next;
    30         }
    31     }
    32 }
    33     init_judge();   //【3】half_SCC判定  【主函数程序段】
    34     for(int cn=1;cn<=num_scc;cn++)
    35         if(in_d[cn] == 0)
    36             root = cn;  //找到入度为0的点,做起点dfs
    37     vis_scc[root]=1;
    38     judge_halfSCC(root,1);
    39 
    40     if(is_halfSCC)    printf("Yes
    ");
    41     else  printf("No
    ");

    【2】方法2(仅适应于kosaraju算法)

          求出缩点后所有顶点的入度ind[]。思考:如果原图G要是半连通的,那么缩点后的图mat必须要连通,这是基础的前提,不然原图都是不连通的,这时只要判断mat中顶点是否只有一个入度为0的点,如果当前的 DAG 有不止一个入度为 0 的点,那么这些点之间是不可到达的,导致图G不是半连通的。此外,mat就是一棵树,入度为0的顶点就是根,如果这个树不是一条链,那么图G也不是半连通的,不是链就说明有分叉,两个分叉之间是不能到达的,那么如何判断是否有分叉呢?答案是拓扑排序,如果排序到某个节点后,剩下的顺序不能确定,就说明出现了分叉。

          其实程序是判断树的高度是否==num_scc。

     1 void judge_half_SCC(void)
     2 {
     3     int i;
     4     ENode *ptr=(ENode *)malloc(sizeof(ENode));
     5 
     6     num_indegree_0=0;
     7     for(i=1;i<=num_scc;i++)
     8     {
     9         if(in_degree[i]==0)
    10             num_indegree_0++;
    11     }
    12 
    13     if(num_indegree_0 > 1)  //优先判断重构图是否连通
    14         printf("No
    ");
    15     else
    16     {
    17         depth=0;  //按层处理
    18         while(1)
    19         {
    20             num_indegree_0=0;
    21             for(i=1;i<=num_scc;i++)
    22                 if(in_degree[i] == 0)
    23                 {
    24                     root=i;
    25                     num_indegree_0++;
    26                 }
    27             if(num_indegree_0>1 || num_indegree_0==0)
    28                 break;
    29 
    30             in_degree[root]=-1; //标记+下层遍历
    31             depth++;
    32             ptr=rebuild_ALG->vlist[root].firstedge;
    33             while(ptr!=NULL)
    34             {
    35                 in_degree[ptr->key]--;
    36                 ptr=ptr->next;
    37             }
    38         }
    39         if(depth==num_scc)  //若相等
    40             printf("Yes
    ");
    41         else  printf("No
    ");
    42     }
    43 }

    求出有向图的最大半连通子图

          tarjan或kosarju缩点之后变为DAG,最大节点数即为最长链,一条链的长度定义为所有节点的权值之和,每个scc的权值为它的节点个数。一个注意的地方就是tarjan之后重构图的时候会加入重边,要消除重边影响。一个SCC里所有点之间都是半连通的。如果两个强连通之间有边,那么这两个强连通中的任意点也是半连通的。

      [1].找出入度为0的点做DFS,并统计count权值

      [2].count是把父亲节点的num向孩子节点加;

      [3].找出count数组中的最大值max_count即可;

      [4].计算出与max_count相等的个数,即max_halfSCC个数

    在程序中,首先要记录下每个SCC包含的顶点个数num[i]。

    
    
     1 /*深度优先搜索寻找最大权值*/
     2 void init_find(void)
     3 {
     4     for(int i=1;i<=num_scc;i++)
     5     {
     6         vis_scc[i]=0;
     7         count[i]=num[i];
     8     }
     9     root=0;
    10 }
    11 void Find_max_halfSCC(int u)
    12 {
    13     ENode *ptr=(ENode *)malloc(sizeof(ENode));
    14     int son;
    15 
    16     ptr=rebuild_ALG->vlist[u].firstedge;
    17     while(ptr!=NULL)
    18     {
    19         son=ptr->key;
    20         if(!vis_scc[son])
    21         {
    22             vis_scc[son]=1;
    23             count[son]+=count[u];
    24             Find_max_halfSCC(son);
    25             vis_scc[son]=0;  //回溯时要用到
    26         }
    27         ptr=ptr->next;
    28     }
    29 }
    30      init_find();
    31     for(int cnt=1;cnt<=num_scc;cnt++)  //【3.判定】
    32         if(in_degree[cnt] == 0)
    33         {
    34             root=cnt;  //找到入度为0的点,做起点DFS
    35             
    36             vis_scc[root]=1;  //也可以拿到if外面,这样只是为了考虑in_d=0个数不止一个的情况
    37             count[root]=num[root];
    38             Find_max_halfSCC(root);
    39         }                                                                                                                                         
    // 然后再执行第[3][4]步即可。至于求解最大半连通子图中的顶点,只要对新图的逆表作dfs即可。 40 void DFS_reverse_rebuild_ALG(int u) 41 { 42 int son; 43 ENode *ptr=(ENode *)malloc(sizeof(ENode)); 44 45 vis_scc[u]=1; //标记+访问+遍历 46 for(int v=0;v<ALG->n;v++) 47 if(u == belong[v]) 48 printf("%c ",ALG->vlist[v].vertex); //输出当前强连通分量u中的顶点 49 ptr=reverse_rebuild_ALG->vlist[u].firstedge; 50 while(ptr!=NULL) 51 { 52 son=ptr->key; 53 if(!vis_scc[son]) 54 { 55 vis_scc[son]=1; 56 DFS_reverse_rebuild_ALG(son); 57 vis_scc[son]=0; 58 } 59 ptr=ptr->next; 60 } 61 } 62 memset(vis_scc,0,sizeof(vis_scc)); 63 for(int ii=1;ii<=num_scc;ii++) //对新图的逆表做一次dfs 64 { 65 if(count[ii] == max_count && !vis_scc[ii]) 66 { 67 vis_scc[ii]=1; 68 DFS_reverse_rebuild_ALG(ii); 69 vis_scc[ii]=0; //回溯时用 70 } 71 printf(" "); 72 }
    ---  纵使山重水复,亦会柳暗花明   sunqh1991@163.com   欢迎关注,互相交流
  • 相关阅读:
    小程序 canvas实现图片预览,图片保存
    MySQL实现排名并查询指定用户排名功能
    微信小程序canvas把正方形图片绘制成圆形
    WINDOW 安装ImageMagick服务端和PHP的imagick插件
    安装PHP扩展32位与64位的误区(x86与x64的查看)
    linux 安装 ImageMagick 和 imagick 扩展
    php 获取顶级域名
    php中通过Hashids将整数转化为唯一字符串
    yii2项目中运行composer 过程中遇到的问题
    yii2 mysql根据多个字段的数据计算排序
  • 原文地址:https://www.cnblogs.com/wjcx-sqh/p/5929924.html
Copyright © 2020-2023  润新知