无向图的割顶和桥,无向图的双连通分量

割顶和桥:对于无向图G,如果删除某个节点u后,连通分量数目增加,则称u为图的割顶;如果删除某条边后,连通分量数目增加,则称该边为图的桥。对于连通图删除割顶或桥后都会使得图不再连通

求图中割顶和桥的代码:


#include<bits/stdc++.h>

using namespace std;

const int maxn =1000;

int n,m;
vector<int> G[maxn];
int low[maxn],dfn[maxn];
int dfs_clock; 
int iscut[maxn]; 

int dfs(int u,int fa)
{
    int lowu = dfn[u] = ++dfs_clock;
    int child = 0;
    for(int i=0;i<G[u].size();i++){
        int v = G[u][i];
        if(!dfn[v]){
            child++;
            int lowv = dfs(v,u);
            lowu = min(lowu,lowv);
            if(lowv >= dfn[u]){
                iscut[u] = 1;
            }
            if(lowv > dfn[u]){
                cout<<"桥:"<<u<<"-"<<v<<endl;
            }
        }else if(dfn[v] < dfn[u] && v != fa){//用反向边更新lowu
            lowu = min(lowu,dfn[v]);
        }
    }
    if(fa < 0 && child == 1) iscut[u] = 0;    //对于根节点的处理
    low[u] = lowu;
    return lowu;
}


int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(dfn,0,sizeof(dfn));
        memset(iscut,0,sizeof(iscut));
        for(int i=0;i<=n;i++){
            G[i].clear();
        }
        int u,v;
        for(int i=0;i<m;i++){
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1,-1);
        for(int i=1;i<=n;i++){
            if(iscut[i]){
                cout<<i<<endl;
            }
        }
    }
    return 0;
}

点_双连通分量 BCC: 
对于一个连通图,如果任意两点至少存在两条“点不重复”的路径,则说图是点双连通的(即任意两条边都在一个简单环中),点双连通的极大子图称为点_双连通分量。

易知每条边属于一个连通分量,且连通分量之间最多有一个公共点,且一定是割顶.

代码讲解:在理解了上面找割顶的代码后,以上求BCC的代码就是用一个栈保存所有的访问的边,然后在找到一个割顶之后就将该割顶信息全部出栈后保存起来即可。


#include<bits/stdc++.h>

using namespace std;

const int maxn = 1000;

struct edge
{
    int v,next;
};
edge edges[maxn];
int head[maxn],e;

struct Edge
{
    int u,v;
    Edge(int uu,int vv){
        u=uu;
        v=vv;
    }
};
stack<Edge> s;

int n,m;
int dfn[maxn];
int dfs_clock;
int iscut[maxn];
int bcc_cnt;
int bccno[maxn];
vector<int>bcc[maxn];

void init()
{
    memset(dfn,0,sizeof(dfn));
    memset(iscut,0,sizeof(iscut));
    memset(head,-1,sizeof(head));
    memset(bccno,0,sizeof(bccno));
    e = 0; dfs_clock = 0; bcc_cnt = 0;
}

void add_edges(int u,int v)
{
    edges[e].v = v;
    edges[e].next = head[u];
    head[u] = e++;
    edges[e].v = u;
    edges[e].next = head[v];
    head[v] = e++;
}

int dfs(int u,int fa)
{
    int lowu = dfn[u] = ++dfs_clock;
    int child = 0;
    for(int i=head[u];i!=-1;i=edges[i].next){
        int v = edges[i].v;
        Edge e = (Edge){u,v};
        if(!dfn[v]){
            s.push(e);
            child++;
            int lowv = dfs(v,u);
            lowu = min(lowu,lowv);
            if(lowv >= dfn[u]){
                iscut[u] = 1;
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                for(;;){
                    Edge x = s.top(); s.pop();
                    if(bccno[x.u] != bcc_cnt){
                        bcc[bcc_cnt].push_back(x.u);
                        bccno[x.u] = bcc_cnt;
                    }
                    if(bccno[x.v] != bcc_cnt){
                        bcc[bcc_cnt].push_back(x.v);
                        bccno[x.v] = bcc_cnt;
                    }
                    if(x.u == u && x.v == v){
                        break;
                    }
                }
            }
        }else if(dfn[v]<dfn[u] && v != fa){
            s.push(e);
            lowu = min(lowu,dfn[v]);
        }
    }
    if(fa < 0 && child == 1){
        iscut[u] = 0;
    }
    return lowu;
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        int u,v;
        for(int i=0;i<m;i++){
            scanf("%d%d",&u,&v);
            add_edges(u,v);
        }
        dfs(1,-1);
        for(int i=1;i<=bcc_cnt;i++){
            if(bcc[i].size()>2){
                for(int j=0;j<bcc[i].size();j++){
                    cout<<bcc[i][j]<<" ";
                }
                cout<<endl;
            }
        }
    }
    return 0;
}

边_双连通分量 EBC: 
对于边_双连通分量的求解简单多了,先找出所有的桥,并将其做上标记。然后在利用dfs遍历连通分量即可,只需在遍历时不能访问桥即可。


#include<bits/stdc++.h>

using namespace std;

const int maxn = 1000;

struct Edge
{
    int no,v,next;
};
Edge edges[maxn];

int n,m;
int e,head[maxn];
int dfn[maxn];
int dfs_clock;
int isbridge[maxn];

int ebcnum;
vector<int> ebc[maxn];

void init()
{
    memset(dfn,0,sizeof(dfn));
    memset(isbridge,0,sizeof(isbridge));
    memset(head,-1,sizeof(head));
    e = 0; ebcnum = 0;
}

void addedges(int num,int u,int v)
{
    edges[e].no = num;
    edges[e].v = v;
    edges[e].next = head[u];
    head[u] = e++;
    edges[e].no = num++;
    edges[e].v = u;
    edges[e].next = head[v];
    head[v] = e++;
}

int dfs_findbridge(int u,int fa)
{
    int lowu = dfn[u] = ++dfs_clock;
    for(int i=head[u];i!=-1;i=edges[i].next){
        int v = edges[i].v;
        if(!dfn[v]){
            int lowv = dfs_findbridge(v,u);
            lowu = min(lowu,lowv);
            if(lowv > dfn[u]){
                isbridge[edges[i].no] = 1;
            }
        }else if(dfn[v] < dfn[u] && v != fa){
            lowu = min(lowu,dfn[v]);
        }
    }
    return lowu;
}

void dfs_coutbridge(int u,int fa)
{
    ebc[ebcnum].push_back(u);
    dfn[u] = ++dfs_clock;
    for(int i=head[u];i!=-1;i=edges[i].next){
        int v = edges[i].v;
        if(!isbridge[edges[i].no]&&!dfn[v]){
            dfs_coutbridge(v,u);
        }
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        int u,v;
        for(int i=0;i<m;i++){
            scanf("%d%d",&u,&v);
            addedges(i,u,v);
        }
        dfs_findbridge(1,-1);
        memset(dfn,0,sizeof(dfn));
        for(int i=1;i<=n;i++){
            if(!dfn[i]){
                ebc[ebcnum].clear();
                dfs_coutbridge(i,-1);
                ebcnum++;
            }
        }
        for(int i=0;i<ebcnum;i++){
            if(ebc[i].size()>1){
                for(int j=0;j<ebc[i].size();j++){
                    cout<<ebc[i][j]<<" ";
                }
                cout<<endl;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/81659960