zoukankan      html  css  js  c++  java
  • 无向图的边双连通分量(EBC)

    嗯,首先边双连通分量(双连通分量之一)是:在一个无向图中,去掉任意的一条边都不会改变此图的连通性,即不存在桥(连通两个边双连通分量的边),称作边双连通分量。一个无向图的每一个极大边双连通子图称作此无向图的双连通分量。

    对于边连通分量,我们需要先找出所有的桥,即为所有的桥做上标记。

    首先要用dfs的性质来快速找出一个连通图中的所有的桥。

    时间戳:表示在进行dfs的时候,每个节点被访问的先后顺序。每个节点会被标记两次,分别用 pre[],和post[]来表示。

    在无向图中,只存在两种边,一种是树边(即边和点都没有被访问过),另一种是反向边(即边没有被访问过,但是点已经被访问过)。所以对于根节点而言,如果有两个及以上节点则根节点为割顶,否则不是 
    对于其他节点:在无向连通图G的DFS树中,非根节点u是割顶当且仅当u存在一个子节点v,使得v及其所有后代都没有反向边连回u的祖先(不包括u) 

    然后设low[u]为u及其后代所能连回的最早的祖先的pre[]值,则当u存在节点v使得low[v] >= pre[u]时,u 就为割顶;

    而同理当 low[v] > pre[u] 时 u-v 是桥。

    接下来直接上求图中割顶和桥的代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <vector>
     5 using namespace std;
     6 
     7 const int maxn = 1005;
     8 int n,m;    //n为点数,m为边数
     9 int dfs_time;   //时间戳
    10 vector<int>G[maxn];
    11 int low[maxn],pre[maxn];
    12 int iscut[maxn];//会标记是否为割顶
    13 
    14 int dfs(int u,int fa){
    15     int lowu = pre[u] = ++dfs_time;
    16     int child = 0;
    17     for(int i = 0;i < G[u].size();i++){
    18         int v = G[u][i];    //v是u所连接的点
    19         if(!pre[v]) //没有访问过
    20         {
    21             child++;    //孩子的节点数
    22             int lowv = dfs(v,u);
    23             lowu = min(lowu,lowv);  //用后代更新lowu
    24 
    25             //是割顶的判断条件
    26             if(lowv >= pre[u])
    27                 iscut[u] = 1;
    28 
    29             //是桥的判断条件
    30             if(lowv > pre[u])
    31                 printf("%d -- %d 是桥
    ",u,v);
    32         }
    33         else if(pre[v] < pre[u] && v != fa){
    34             //是反向边的情况,就更新lowu
    35             lowu = min(lowu,pre[v]);
    36         }
    37         return lowu;    //返回当前节点及其子节点能回到的最早祖先的pre值
    38     }
    39 }
    40 
    41 int main(){
    42     while(scanf("%d%d",&n,&m)!=EOF){
    43         memset(pre,0,sizeof(pre));
    44         memset(iscut,0,sizeof(iscut));
    45         for(int i = 0;i <= n;i++)
    46             G[i].clear();
    47         int u,v;    //u -> v
    48         for(int i = 0;i < m;i++){
    49             scanf("%d%d",&u,&v);
    50             G[u].push_back(v);  //在u中添加v
    51             G[v].push_back(u);  //在v中添加u(因为是无向图)
    52         }
    53         dfs(1,-1);  //u是当前节点,fa是父节点
    54         printf("割顶有:");
    55         for(int i = 1;i <= n;i++){
    56             if(iscut[i])    //如果是割顶
    57                 printf("%d ",i);
    58         }
    59     }
    60     return 0;
    61 }

    第一步已经完成(对桥做标记)。然后利用dfs遍历连通分量,只不过在遍历的时候不能访问桥。

    上代码:

      1 #include <cstdio>
      2 #include <algorithm>
      3 #include <cstring>
      4 #include <vector>
      5 using namespace std;
      6 
      7 const int maxn = 1000;
      8 struct Edge
      9 {
     10     int no,v,next;      //no:边的编号
     11 }edges[maxn];
     12 
     13 int n,m,ebcnum;         //节点数目,无向边的数目,边_双连通分量的数目
     14 int e,head[maxn];
     15 int pre[maxn];          //第一次访问的时间戳
     16 int dfs_clock;          //时间戳
     17 int isbridge[maxn];     //标记边是否为桥
     18 vector<int> ebc[maxn];  //边_双连通分量
     19 
     20 void addedges(int num,int u,int v)    //加边
     21 {
     22     edges[e].no = num;
     23     edges[e].v = v;
     24     edges[e].next = head[u];
     25     head[u] = e++;
     26     edges[e].no = num++;
     27     edges[e].v = u;
     28     edges[e].next = head[v];
     29     head[v] = e++;
     30 }
     31 
     32 int dfs_findbridge(int u,int fa)    //找出所有的桥
     33 {
     34     int lowu = pre[u] = ++dfs_clock;
     35     for(int i=head[u];i!=-1;i=edges[i].next)
     36     {
     37         int v = edges[i].v;
     38         if(!pre[v])
     39         {
     40             int lowv = dfs_findbridge(v,u);
     41             lowu = min(lowu,lowv);
     42             if(lowv > pre[u])
     43             {
     44                 isbridge[edges[i].no] = 1; //
     45             }
     46         }
     47         else if(pre[v] < pre[u] && v != fa)
     48         {
     49             lowu = min(lowu,pre[v]);
     50         }
     51     }
     52     return lowu;
     53 }
     54 
     55 void dfs_coutbridge(int u,int fa)     //保存边_双连通分量的信息
     56 {
     57     ebc[ebcnum].push_back(u);
     58     pre[u] = ++dfs_clock;
     59     for(int i=head[u];i!=-1;i=edges[i].next)
     60     {
     61         int v = edges[i].v;
     62         if(!isbridge[edges[i].no] && !pre[v]) dfs_coutbridge(v,u);
     63     }
     64 }
     65 
     66 void init()
     67 {
     68     memset(pre,0,sizeof(pre));
     69     memset(isbridge,0,sizeof(isbridge));
     70     memset(head,-1,sizeof(head));
     71     e = 0;
     72     ebcnum = 0;
     73 }
     74 
     75 int main()
     76 {
     77     int u,v;
     78     while(scanf("%d%d",&n,&m)!=EOF)
     79     {
     80         init();
     81         for(int i=0;i<m;i++)
     82         {
     83             scanf("%d%d",&u,&v);
     84             addedges(i,u,v);
     85         }
     86         dfs_findbridge(1,-1);   //进行找桥
     87         memset(pre,0,sizeof(pre));
     88         for(int i=1;i<=n;i++)
     89         {
     90             if(!pre[i])
     91             {
     92                 ebc[ebcnum].clear();
     93                 dfs_coutbridge(i,-1);
     94                 ebcnum++;
     95             }
     96         }
     97         for(int i=0;i<ebcnum;i++)
     98         {
     99             for(int j=0;j<ebc[i].size();j++)
    100                 printf("%d ",ebc[i][j]);
    101             printf("
    ");
    102         }
    103     }
    104     return 0;
    105 }
  • 相关阅读:
    nginx负载均衡及配置
    MySQL中的锁(表锁、行锁)
    Spring框架IOC容器和AOP解析
    六个绝佳的PHP模板引擎
    Linux下Redis的安装和部署
    PHP5.6 和PHP7.0区别
    数据库主从分离
    TCP三次握手四次挥手
    JS鼠标获取坐标
    thinkphp1
  • 原文地址:https://www.cnblogs.com/ouyang_wsgwz/p/7688739.html
Copyright © 2011-2022 走看看