返回顶部

连通性问题——双连通分量——割点和割边

参考自:OI Wiki

定义

割点和桥更严谨的定义参见 图论相关概念

在一张连通的无向图中,对于两个点u和v,如果无论删去哪条边(只能删去一条)都不能使它们不连通,我们就说u和v边双连通

在一张连通的无向图中,对于两个点u和v,如果无论删去哪个点(只能删去一个,且不能删u和v自己)都不能使它们不连通,我们就说u和v点双连通

边双连通具有传递性,即,若x,y边双连通,y,z边双连通,则x,z边双连通。

点双连通  具有传递性,反例如下图,A,B点双连通,B,C点双连通,而A,C点双连通。

bcc-counterexample.png

割点

对于一个无向图,如果把一个点删除后这个图的极大连通分量数增加了,那么这个点就是这个图的割点(又称割顶)。

割边

和割点差不多,叫做桥。

对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。严谨来说,就是:假设有连通图G={V,E},e是其中一条边(即e∈E),如果G--e是不连通的,则边e是图G的一条割边(桥)。

例题:

P3388 【模板】割点(割顶) ——割点

复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+5;
const int M=2e5+5;
int n,m,tot,num,cnt,top;
int dfn[N],low[N],z[N],ans[N],d[N];
int fi[N],ne[M],to[M];

void add(int x,int y)
{
    ne[++tot]=fi[x];
    fi[x]=tot;
    to[tot]=y;
}

void dfs(int x,int y)
{
    dfn[x]=low[x]=++num,z[++top]=x;
    for(int i=fi[x];i;i=ne[i])
    {
        int v=to[i],vv;
        if(v==y) continue;
        if(!dfn[v])
        {
            dfs(v,x);
            low[x]=min(low[x],low[v]);
            if(low[v]>=dfn[x])
            {
                d[x]++;
                do
                {
                    vv=z[top--];
                    d[vv]++;
                }while(vv!=v);
            }
        }
        else
            low[x]=min(low[x],dfn[v]);
    }
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        cin>>a>>b;
        add(a,b),add(b,a);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            dfs(i,-1);
    for(int i=1;i<=n;i++)
        if(d[i]>1)
            ans[++cnt]=i;
    cout<<cnt<<'\n';
    for(int i=1;i<=cnt;i++)
        cout<<ans[i]<<' ';
}
复制代码

P1656 炸铁路 ——割边

复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n,m,tot,num,cnt;
int fi[N],ne[N],to[N];
int dfn[155],low[155];
struct xiao
{
    int a,b;
}ans[155];

bool node(xiao x,xiao y)
{
    if(x.a==y.a)
        return x.b<y.b;
    return x.a<y.a;
}

void add(int x,int y)
{
    ne[++tot]=fi[x];
    fi[x]=tot;
    to[tot]=y; 
}

void dfs(int x,int y)
{
    dfn[x]=low[x]=++num;
    for(int i=fi[x];i;i=ne[i])
    {
        int v=to[i];
        if(v==y) continue;
        if(!dfn[v])
        {
            dfs(v,x);
            low[x]=min(low[x],low[v]);
            if(low[v]>dfn[x])
                ans[++cnt].a=min(x,v),ans[cnt].b=max(x,v);
        }
        else
            low[x]=min(low[x],dfn[v]);
    }
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        cin>>a>>b;
        add(a,b),add(b,a);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            dfs(i,0);
    sort(ans+1,ans+cnt+1,node);
    for(int i=1;i<=cnt;i++)
        cout<<ans[i].a<<' '<<ans[i].b<<'\n';    
}
复制代码


如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
posted @   光暗之影x  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示