无向连通图的割点和桥

无向连通图的割点、桥

泳裤王子原创,转载请注明出处 http://blog.csdn.net/tclh123/article/details/6705392

预备知识

       割点集合

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

       割边集合

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

 

连通度

       点连通度

       一个图的点连通度的定义为,最小割点集合中的顶点数。

       边连通度

       一个图的边连通度的定义为,最小割边集合中的边数。

       双连通图

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

 

求割点与桥

       概念:

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

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

       求法:

              使用dfs(深搜)来求割点和桥。先明确一下几点:

1、  图的dfs相当于是对相应的dfs树的遍历。

2、  无向图的dfs树,无论以哪个点为根都可以遍历完所有的点。

3、  无向图的dfs树,没有横叉边(连接两个子树的边)。

(以下结合BYVoid牛神文)

定义dfn[u]为u在dfs搜索树(以下简称为树)中被遍历到的次序号。

定义low[u]为u或u的子树中能通过非父子边追溯到的最早的节点,即dfn[]最小的节点。则有:

low[u]=Min

 {

 dfn[u],

dfn[v] ,// (u,v)为后向边(返祖边) , 等价于 dfn[v]<dfn[u]且v不为u的父亲节点

 low[v], //(u,v)为树枝边(父子边)

 }

所以决定low[u]的关键在于 ,子孙有没有返祖边,返祖边到达的高度是否比dfn[u]小。

//-----------------------------------------------------------------------

①割点u,当且仅当满足(1)或(2)

 (1) u为树根,且u有多于一个子树。

 (2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得dfn[u]<=low[v]

 

②桥无向边(u,v),当且仅当(u,v)为树枝边,且满足dfn[u]<low[v]

 

       注:1、为方便程序编写,我们都采用low[v]来判断u。(理论上low[u]也可以判断割点)

 

       例图:

  

       Dfs树:

 

       伪代码:

       Init()

              Dfn[~]= invis[~] = 0;

              RootChild= 0;

       Dfs(u,father)

              Dfn[u]= low[u] = ++index;

              Invis[u]= true;

              Each (u, v)

                     Ifdfn[v]=0            //(u,v)父子边

                            Dfs(v,u)

                            Ifu=Root

                                   RootChild++;

                            Elseif dfn[u]<=low[v]

                                   u is cut

                            Ifdfn[u]<low[v]

                                   (u, v) is brige

                            Low[u] = min(low[u], low[v]);

                     Elseif v!=father && invis[v]=true              //(u,v)返祖边

                            Low[u]= min(low[u], dfn[v]);

                     Invis[u]= false;

              Return;

 

示例代码:

[cpp]  view plain copy
  1. //无向连通图bfs  求   割点、桥  
  2. //中间改了几次,写得较烂  
  3. #include<cstdio>  
  4. #include<cstring>  
  5. #include<iostream>  
  6. using namespace std;  
  7. #define FF(x1, x2) for(int i=x1; i<x2; i++)  
  8. #define MAXN 100  
  9. #define MAXM 100  
  10. int dfn[MAXN], low[MAXN];       //low  
  11. struct edge{int u, v; }a[MAXM];  
  12. int first[MAXN], next[MAXM];  
  13. int n, m;  
  14. int num;  
  15.   
  16. int cut[MAXN], cn;  //割点  
  17. edge brige[MAXM];   int bn;//桥  
  18.   
  19. int invis[MAXN];        //正在访问  
  20. //也可以统统用颜色标记点,白、灰、黑!!!!!!  
  21.   
  22. int root;  
  23. int dfs(int u, int father)      //把 father入栈,避免走反父子边  
  24. {  
  25.     invis[u] = 1;  
  26.     int child=0;  
  27.     dfn[u] = low[u] = ++num;        //++num !!!!!!!!!!!!!!!!!!!①  
  28.     for(int e=first[u]; e!=-1; e=next[e])  
  29.     {  
  30.         int v = a[e].v;  
  31.         if(!dfn[v]) //父子边  
  32.         {  
  33.             dfs(v, u);  
  34.   
  35.             child++;  
  36.             if(u!=root && dfn[u]<=low[v]) cut[cn++] = u;                 //cut       //要求不是根结点 !!!!!!!!!③  
  37.             if(dfn[u]<low[v]) brige[bn].u=u, brige[bn++].v=v;        //brige  
  38.               
  39.             low[u] = low[u]<low[v]? low[u]: low[v];  
  40.         }  
  41.         else if(v != father && invis[v])//反向边       if(v != father) 由于是无向图!确保不要走反父子边  
  42.         {  
  43.             low[u] = low[u]<dfn[v]? low[u]: dfn[v];  
  44.         }  
  45.     }  
  46.     invis[u] = 0;  
  47.     return child;  
  48. }  
  49.   
  50. void print()  
  51. {  
  52.     cout<<"cut cn="<<cn<<endl;  
  53.     FF(0, cn)  
  54.     {  
  55.         cout<<i<<"\t"<<cut[i]<<endl;  
  56.     }  
  57.     cout<<"brige bn="<<bn<<endl;  
  58.     FF(0, bn)  
  59.     {  
  60.         cout<<i<<"\t"<<"["<<brige[i].u<<","<<brige[i].v<<"]"<<endl;  
  61.     }  
  62. }  
  63.   
  64. void addedge(int u, int v, int e){  next[e] = first[u]; a[e].u = u; a[e].v = v; first[u] = e;       }  
  65. void read_graph() { memset(first, -1, sizeof(first)); cin>>n>>m; FF(0, m){ int u, v; cin>>u>>v; addedge(u, v, i); addedge(v, u, i+m); } }//无向边  
  66. int main()  
  67. {  
  68.     cn = bn = 0;  
  69.     num=0;      //dfs 的访问 num,用来初始化 dfn  
  70.     memset(dfn, 0, sizeof(dfn));  
  71.     memset(invis, 0, sizeof(invis));  
  72.     read_graph();  
  73.     /* 
  74. //  FF(0, n) 
  75.     FF(1, n+1)      //  1-index 
  76.     { 
  77.         if(!dfn[i]) //未访问 
  78.         { 
  79.             if(dfs(i, i)>1) cut[cn++]=i; // u为树根,且u有多于一个子树。 
  80.         } 
  81.     } 
  82.     */  
  83.     //       无向连通图 bfs 指定任意根一点,必定遍历完。       !!!!!!!!与有向图不一样④  
  84.     root=1;  
  85.     if(dfs(1, 1)>1) cut[cn++]=1; // u为树根,且u有多于一个子树。  
  86.     print();  
  87. }  
  88. /*          Demo~ 
  89. input 
  90. 6 6 
  91. 1 2 
  92. 2 4 
  93. 4 1 
  94. 2 3 
  95. 3 5 
  96. 5 6 
  97.  
  98. output 
  99. cut cn=3 
  100. 0   5 
  101. 1   3 
  102. 2   2 
  103. brige bn=3 
  104. 0   [5,6] 
  105. 1   [3,5] 
  106. 2   [2,3] 
  107.  
  108. */  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值