zoukankan      html  css  js  c++  java
  • 双连通问题

    一些定义:

    割点集合(割集):在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合。

    点连通度:最小割点集合中的顶点数。

    割边集合:如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合。

    边连通度:最小割边集合中的边数。

    点双连通:如果一个无向连通图的点连通度大于1,则称该图是点双连通的,简称双连通或重连通。
    割点:一个图有割点,当且仅当这个图的点连通度为1,则割点集合的唯一元素被称为割点,又叫关节点。

    边双连通:如果一个无向连通图的边连通度大于1,则称该图是边双连通的,简称双连通或重连通。

    割边(桥):一个图有桥,当且仅当这个图的边连通度为1,则割边集合的唯一元素被称为桥,又叫关节边。

    在图G的所有子图G'中,如果G'是双连通的,则称G'为双连通子图。如果一个双连通子图G'它不是任何一个双连通子图的真子集,则G'为极大双连通子图。双连通分支,或重连通分支,就是图的极大双连通子图。特殊的,点双连通分支又叫做块。

    求割点与桥

    该算法是R.Tarjan发明的。对图深度优先搜索,定义DFS(u)为u在搜索树(以下简称为树)中被遍历到的次序号。定义Low(u)为u或u的子树中能通过非父子边追溯到的最早的节点,即DFS序号最小的节点。根据定义,则有:

    Low(u)=Min { DFS(u) DFS(v) (u,v)为后向边(返祖边) 等价于 DFS(v)<DFS(u)且v不为u的父亲节点 Low(v) (u,v)为树枝边(父子边) }

    一个顶点u是割点,当且仅当满足(1)或(2) (1) u为树根,且u有多于一个子树。 (2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)。

    条无向边(u,v)是,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)。

    下面的是求割边与割点的模版:

     1 const int N =1010, M=100010;
     2 struct Edge
     3 {
     4     int to, next;
     5     bool iscut;
     6 }edge[M];
     7 int head[N],tot, low[N], dfn[N], sta[N];
     8 int add_block[N];//删除一个点以后增加的连通块
     9 bool insta[N],cut[N];
    10 int top,sig,bridge,child;
    11 void tarjanbfs(int u, int from)
    12 {
    13     sta[top++]=u;
    14     dfn[u]=++sig; low[u]=sig;
    15     insta[u]=1;
    16     child=0;
    17     for(int i=head[u];i!=-1;i=edge[i].next)
    18     {
    19         int v=edge[i].to;
    20         if(v ==from) continue;
    21         if(!dfn[v])
    22         {
    23             child++;
    24             tarjanbfs(v,u);
    25             low[u]=min(low[u],low[v]);
    26             if(dfn[u]<low[v]) //判断桥的条件
    27             {
    28                 bridge++;
    29                 edge[i].iscut=1;
    30                 edge[i^1].iscut=1;
    31             }
    32             if(u!=from&&low[v]>=dfn[u]) // 割点
    33             {
    34                 cut[u]=1;
    35                 add_block[u]++;
    36             }
    37         }
    38         else low[u]=min(low[u],low[v]);
    39     }
    40     if(u==from && child>1) cut[u]=1; //这也是割点
    41     if(u==from) add_block[u]=child -1;
    42     insta[u]=0;
    43     top--;
    44 }
    45 void tarjan(int n)
    46 {
    47     memset(insta,0,sizeof(insta));
    48     memset(add_block,0,sizeof(add_block));
    49     memset(dfn,0,sizeof(dfn));
    50     memset(cut,0,sizeof(cut));
    51     sig=top=0; bridge=0;
    52     for(int i=1;i<=n;i++)
    53         if(!dfn[i]) tarjanbfs(i,i);
    54 }
    55 void addedge(int i,int j)
    56 {
    57     edge[tot].to=j;edge[tot].next=head[i];
    58     edge[tot].iscut=0;head[i]=tot++;
    59 }
    60 void init()
    61 {
    62     memset(head,-1,sizeof(head));
    63     tot=0;
    64 }
    View Code

    一些题目:

    hdu4587  TWO NODES

    题意:求任意删除两个点以后,可以得到的最多的连通分支数

    枚举删除一条边后产生的割点,求删除该割点(或许有多个割点)产生的连通分支数。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 const int N =5010, M=10010;
     8 struct Edge
     9 {
    10     int to, next;
    11 }edge[M];
    12 int head[N],tot, low[N], dfn[N], sta[N];
    13 int add_block[N];//删除一个点以后增加的连通块
    14 bool insta[N],cut[N];
    15 int top,sig,cnt,tt,ans;
    16 void tarjanbfs(int u, int from)
    17 {
    18     sta[top++]=u;
    19     dfn[u]=++sig; low[u]=sig;
    20     insta[u]=1;
    21     int child=0;
    22     for(int i=head[u];i!=-1;i=edge[i].next)
    23     {
    24         int v=edge[i].to;
    25         if(v ==from) continue;
    26         if(v ==tt) continue;
    27         if(!dfn[v])
    28         {
    29             child++;
    30             tarjanbfs(v,u);
    31             low[u]=min(low[u],low[v]);
    32             if(u!=from&&low[v]>=dfn[u]) // 割点
    33             {
    34                 cut[u]=1;
    35                 add_block[u]++;
    36             }
    37         }
    38         else if(insta[v]) low[u]=min(low[u],dfn[v]);
    39     }
    40     if(u==from && child>1) cut[u]=1; //这也是割点
    41     if(u==from) add_block[u]=child -1;
    42     insta[u]=0;
    43     top--;
    44 }
    45 void tarjan(int n)
    46 {
    47     memset(insta,0,sizeof(insta));
    48     memset(add_block,0,sizeof(add_block));
    49     memset(dfn,0,sizeof(dfn));
    50     memset(cut,0,sizeof(cut));
    51     sig=top=0;
    52     ans=cnt=0;
    53     for(int i=1;i<=n;i++)
    54         if(i!=tt&&!dfn[i])
    55         {
    56             tarjanbfs(i,i);
    57             cnt++;
    58         }
    59     for(int i=1;i<=n;i++)
    60         if(i!=tt)
    61         {
    62             ans=max(ans,cnt+add_block[i]);
    63         }
    64 }
    65 void addedge(int i,int j)
    66 {
    67     edge[tot].to=j;edge[tot].next=head[i];head[i]=tot++;
    68 }
    69 void init()
    70 {
    71     memset(head,-1,sizeof(head));
    72     tot=0;
    73 }
    74 
    75 int main()
    76 {
    77     //freopen("test.txt","r",stdin);
    78     int n,m,i,j,k;
    79     while(scanf("%d%d",&n,&m)!=EOF)
    80     {
    81         init();
    82         while(m--)
    83         {
    84             scanf("%d%d",&i,&j);
    85             i++;j++;
    86             addedge(i,j);
    87             addedge(j,i);
    88         }
    89         int s=0;
    90         for(i=1;i<=n;i++)
    91         {
    92             tt=i;
    93             tarjan(n);
    94             s=max(s,ans);
    95         }
    96         printf("%d
    ",s);
    97     }
    98     return 0;
    99 }
    View Code

    UVA 796 Critical Links

    题意:给出一幅无向图,求出割边的数目,以及输出割边(输出有顺序)。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<vector>
      5 #include<algorithm>
      6 using namespace std;
      7 const int N =1010, M=100010;
      8 struct Edge
      9 {
     10     int to, next;
     11     bool iscut;
     12 }edge[M];
     13 int head[N],tot, low[N], dfn[N], sta[N];
     14 int add_block[N];//删除一个点以后增加的连通块
     15 bool insta[N],cut[N];
     16 int top,sig,bridge,child;
     17 void tarjanbfs(int u, int from)
     18 {
     19     sta[top++]=u;
     20     dfn[u]=++sig; low[u]=sig;
     21     insta[u]=1;
     22     for(int i=head[u];i!=-1;i=edge[i].next)
     23     {
     24         int v=edge[i].to;
     25         if(v ==from) continue;
     26         if(!dfn[v])
     27         {
     28             child++;
     29             tarjanbfs(v,u);
     30             low[u]=min(low[u],low[v]);
     31             if(dfn[u]<low[v]) //判断桥的条件
     32             {
     33                 bridge++;
     34                 edge[i].iscut=1;
     35                 edge[i^1].iscut=1;
     36             }
     37             if(u!=from&&low[v]>=dfn[u]) // 割点
     38             {
     39                 cut[u]=1;
     40                 add_block[u]++;
     41             }
     42         }
     43         else low[u]=min(low[u],low[v]);
     44     }
     45     if(u==from && child>1) cut[u]=1; //这也是割点
     46     if(u==from) add_block[u]=child -1;
     47     insta[u]=0;
     48     top--;
     49 }
     50 void tarjan(int n)
     51 {
     52     memset(insta,0,sizeof(insta));
     53     memset(add_block,0,sizeof(add_block));
     54     memset(dfn,0,sizeof(dfn));
     55     memset(cut,0,sizeof(cut));
     56     sig=top=0; bridge=0;
     57     for(int i=1;i<=n;i++)
     58         if(!dfn[i]) tarjanbfs(i,i);
     59 }
     60 void addedge(int i,int j)
     61 {
     62     edge[tot].to=j;edge[tot].next=head[i];
     63     edge[tot].iscut=0;head[i]=tot++;
     64 }
     65 void init()
     66 {
     67     memset(head,-1,sizeof(head));
     68     tot=0;
     69 }
     70 vector<pair<int,int> >res;
     71 int main()
     72 {
     73     //freopen("test.txt","r",stdin);
     74     int n,i,j,k,t;
     75     char ch;
     76     while(scanf("%d",&n)!=EOF)
     77     {
     78         init();
     79         res.clear();
     80         for(t=1;t<=n;t++)
     81         {
     82 
     83             scanf("%d (%d)",&i,&k);
     84             i++;
     85             while(k--){
     86                 scanf("%d",&j);
     87                 j++;
     88                 if(j<=i) continue;
     89                 addedge(i,j);
     90                 addedge(j,i);
     91             }
     92         }
     93         tarjan(n);
     94         printf("%d critical links
    ",bridge);
     95         for(i=1;i<=n;i++)
     96         {
     97             for(k=head[i];k!=-1;k=edge[k].next)
     98             {
     99                 if(edge[k].iscut&&edge[k].to>i)
    100                     res.push_back(make_pair(i,edge[k].to));
    101             }
    102         }
    103         sort(res.begin(),res.end());
    104         for(i=0;i<res.size();i++)
    105             printf("%d - %d
    ",res[i].first-1,res[i].second-1);
    106         printf("
    ");
    107     }
    108     return 0;
    109 }
    View Code

    poj3177  Redundant Paths

    双连通构造问题,比较热的题目

    题意:就是给了一个连通图,问加多少条边可以变成边双连通

    详细题解:http://blog.csdn.net/wangjian8006/article/details/7990666

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<vector>
     5 #include<algorithm>
     6 using namespace std;
     7 
     8 const int N =5010, M=100010;
     9 struct Edge
    10 {
    11     int to, next;
    12     bool iscut;
    13 }edge[M];
    14 int head[N],tot, low[N], dfn[N], sta[N], belg[N];
    15 int add_block[N];//删除一个点以后增加的连通块
    16 bool insta[N],cut[N];
    17 int top,sig,bridge,block;//边连通块数
    18 int de[N];//度数
    19 void tarjanbfs(int u, int from)
    20 {
    21     int v;
    22     sta[top++]=u;
    23     dfn[u]=++sig; low[u]=sig;
    24     insta[u]=1;
    25     for(int i=head[u];i!=-1;i=edge[i].next)
    26     {
    27         v=edge[i].to;
    28         if(v ==from) continue;
    29         if(!dfn[v])
    30         {
    31             tarjanbfs(v,u);
    32             low[u]=min(low[u],low[v]);
    33             if(dfn[u]<low[v]) //判断桥的条件
    34             {
    35                 bridge++;
    36                 edge[i].iscut=1;
    37                 edge[i^1].iscut=1;
    38             }
    39         }
    40         else if(insta[v]) low[u]=min(low[u],low[v]);
    41     }
    42     if(low[u]==dfn[u])
    43     {
    44         block++;
    45         do
    46         {
    47             v=sta[--top];
    48             insta[v]=0;
    49             belg[v]=block;
    50         }
    51         while(v!=u) ;
    52     }
    53 }
    54 void tarjan(int n)
    55 {
    56     memset(insta,0,sizeof(insta));
    57     memset(add_block,0,sizeof(add_block));
    58     memset(dfn,0,sizeof(dfn));
    59     memset(cut,0,sizeof(cut));
    60     sig=top=0; bridge=0;
    61     tarjanbfs(1,1);
    62 }
    63 void addedge(int i,int j)
    64 {
    65     edge[tot].to=j;edge[tot].next=head[i];
    66     edge[tot].iscut=0;head[i]=tot++;
    67 }
    68 void init()
    69 {
    70     memset(head,-1,sizeof(head));
    71     tot=0;
    72 }
    73 int main()
    74 {
    75     //freopen("test.txt","r",stdin);
    76     int n,m,i,j,k,t,ans;
    77     while(scanf("%d%d",&n,&m)!=EOF)
    78     {
    79         init();
    80         while(m--)
    81         {
    82             scanf("%d%d)",&i,&j);
    83             addedge(i,j);
    84             addedge(j,i);
    85         }
    86         tarjan(n);
    87         memset(de,0,sizeof(de));
    88         ans=0;
    89         for(i=1;i<=n;i++)
    90             for(k=head[i];k!=-1;k=edge[k].next)
    91                 if(edge[k].iscut) de[belg[i]] ++;
    92         for(i=1;i<=block;i++)
    93             if(de[i]==1) ans++;
    94         printf("%d
    ",(ans+1)/2);
    95     }
    96     return 0;
    97 }
    View Code

     POJ 3694 Network

    题意:给出一幅无向图,问在加入新边的过程中桥的数目的变化。

    PS:LCA太烦了,我试着去做此题。然后放弃了。

    PS:双连通的题目一般不出现,出现了就基本上没多少队伍做得出来。

  • 相关阅读:
    使用Python验证常见的50个正则表达式
    空气开关为何叫空气开关?它跟空气有何关系?
    YOLO 算法最全综述:从 YOLOv1 到 YOLOv5
    深入理解部分 SQL 语句执行慢的原因
    KNN(k-nearest-neighbor)算法
    聚类分析方法
    SQL Database学习笔记
    statistic学习笔记
    MapReduce中的排序
    weka打开提示内存不足的解决方法
  • 原文地址:https://www.cnblogs.com/Potato-lover/p/4001179.html
Copyright © 2011-2022 走看看