图论:割点和桥

一、相关概念

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<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
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<y?x:y;
}
void AssignNum(int v) //建立dfs深搜树 
{
    int i,w;
    num[v]=cnt++;
    vis[v]=1;
    for(i=0;i<vc[v].size();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<vc[v].size();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<vc[v].size();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<m;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
*/
View Code

三、桥的求解方法

一条无向边(u,v)是桥当且仅当(u,v)是深搜树的树枝边且满足Num(u)< Low(v)。

            if(num[v]<low[w]) printf("---The edge (%d,%d) is artedge\n",v,w);  //求割边 

猜你喜欢

转载自www.cnblogs.com/2018zxy/p/10357435.html
今日推荐