双连通问题

一些定义:

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

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

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

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

点双连通:如果一个无向连通图的点连通度大于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\n",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\n",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\n",res[i].first-1,res[i].second-1);
106         printf("\n");
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\n",(ans+1)/2);
95     }
96     return 0;
97 }
View Code

 

 POJ 3694 Network

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

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

 

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

 

posted @ 2014-09-29 23:49  pengmq  阅读(621)  评论(0编辑  收藏  举报