一、相关概念
1、点连通度:最小V的点数(一个图的点的连通度是最小割点集合中的顶点数)
2、边连通度:最小E的边数(一个图的边的连通度是最小割边集合中的顶点数)
3、割点:去掉割点这个图不连通(点连通度为1时,V的唯一元素)
4、割边(桥):去掉割边这个图不连通(边连通度为1时,E的唯一元素)
5、双连通图:
如果一个无向图的点连通度大于1,则是点双联通;
如果一个无向图的边连通度大于1,则是边双联通。
6、双联通分量:
在图G中,G’是G的子图,G’双联通,则G’是双联通分量。
(双联通分量一定是点双联通分量,但不一定是边双联通分量)
7、双连通性:将无向图的任意顶点删除后,剩下的图依然连通,那么这样的图是双联通的。
二、割点的求解方法
1、无向图的深度优先生成树:
将对无向图进行深度优先遍历的顺序作为树节点的编号,建立正向边;
相反的,如果遍历到已经存在的节点,建立相应的背向边。
2、求解割点的过程:
(1)建立Num(v),按照深搜的顺序给节点编号(先序遍历编号)
(2)建立Low(v)的三原则:(后序遍历)
Num(v);
所有背向边(v,w)中最低的Num(w);
所有边(v,w)中最低的Low(w);
(3)割点的条件
如果是根节点:有一个以上的儿子;
如果不是根节点:Low(v)>= Num(v)。
(具体实现的细节:
(1)可以让根节点就是割点然后建立深搜树,再求割点;
(2)也可以记录每个节点的儿子的数量然后分别判断根节点与非根节点)。
3、实现:
#include#include #include #include using namespace std; const int maxn = 1200; const int INF = 0x3ffffff; int num[maxn],parent[maxn],low[maxn],vis[maxn],cnt; vector <int> vc[maxn]; int MIN(int x,int y) { return x x:y; } void AssignNum(int v) //建立dfs深搜树 { int i,w; num[v]=cnt++; vis[v]=1; for(i=0;i ){ w=vc[v][i]; if(vis[w]==0){ parent[w]=v; AssignNum(w); } } } void AssignLow(int v) //建立low数组,打印割点 { int i,w; low[v]=num[v]; for(i=0;i ){ w=vc[v][i]; if(num[w]>num[v]){ AssignLow(w); if(low[w]>=num[v]){ printf("The gedian is %d\n",v); } low[v]=MIN(low[v],low[w]); } else if(parent[v]!=w){ low[v]=MIN(low[v],num[w]); } } } void FindArt(int v) //打印割点 = AssignNum+AssignLow; { int i,w; low[v]=num[v]=cnt++; vis[v]=1; for(i=0;i ){ w=vc[v][i]; if(!vis[w]){ parent[w]=v; FindArt(w); if(low[w]>=num[v]) printf("The articulation point is %d\n",v); low[v]=MIN(low[v],low[w]); } else if(parent[v]!=w) low[v]=MIN(low[v],num[w]); } } int main(void) { int n,m,i,j,x,y; cin>>n>>m; cnt=1; memset(vis,0,sizeof(vis)); for(i=0;i ){ scanf("%d%d",&x,&y); vc[x].push_back(y); vc[y].push_back(x); } //AssignNum(3); //AssignLow(3); FindArt(4); //根节点是割点 return 0; } /* 7 8 1 2 2 3 3 4 4 1 4 7 4 5 7 5 3 6 */
三、桥的求解方法
一条无向边(u,v)是桥当且仅当(u,v)是深搜树的树枝边且满足Num(u)< Low(v)。
if(num[v]"---The edge (%d,%d) is artedge\n",v,w); //求割边