【割边&割点】 ◆HihoCoder 1183◆ 连通性一·割边与割点

本文详细介绍了如何通过DFS算法来识别图中的割边与割点,并提供了完整的代码实现。通过对DFN图的理解,读者可以掌握如何判断一个点或一条边是否为割点或割边。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

◆HihoCoder 1183◆

连通性一·割边与割点


□题目□

HihoCoder的题目都这么长吗?(>_<)作者还是不要copy了,直接上网址吧:

戳这里→HihoCoder 1183


□解析□

很明显,这就是一道版题……虽然是板,但是毕竟是比较考验思维的题。
1. 割点与割边

它们的定义非常简单——在一个连通图中,若删去点u会将原图分成2个及以上的连通图,则称u为割点;若删去边F会将原图分成2个及以上的连通图,则称F为割边。
2. DFN图

DFN图是通过用DFS实现的将一个连通图变为一棵树并为所有点按照遍历顺序编号,DFS遍历时不一定要按照从小到大的顺序,按输入顺序即可。
如:DFN样例

所谓树边就是在DFS中从一个已经访问过的节点到达一个未访问过的节点所通过的边,原连通图中除了树边就是回边,或者可以这样理解——若在遍历中寻找与所在点u相邻的点v时,访问到了一个访问过的节点,且v不是DFN树中u的父亲,则那条边就是回边。

  1. 树边与回边

定义如前所示,那么我们需要定义如下数组:

vector<int> vec[MAXN+5];
set<int> ans;
struct Fans{int u,v;}fans[MAXN+20000];int n_fans;
int dfn[MAXN+5],fa[MAXN+5],low[MAXN+5];
  1. vec[] 是vector形式的邻接表(无向);
  2. ans 是用set存储的割点答案,自动排序、去重;
  3. fans[] 是结构体存储的割边答案;
  4. dfn[u] 表示u点在dfs中访问到的点数;
  5. fa[u] 表示在dfs中第一次访问u点时上一个访问的点,即dfn树中u点的编号;
  6. low[u] 表示在原图中从u点出发,只经过回边能够访问的所有的点的最小的dfn值;

其中low和dfn是比较重要的两个值——也就是可以通过low和dfn得出该点是否是割点(或者割边)——在dfn树中,u是v的父亲,则若low[v]>=dfn[u],则该点为割点,而若low[v]>=dfn[u],则边 u->v 是一条割边。

上图的值如下:

123456
dfn[]162345
low[]111333

这里有low[4]>=dfn[3],low[5]>=dfn[4],所以点3、4是割点,而仅有low[4]>dfn[3],所以3->4是割边。


□代码□

en……代码就在这儿……你们敢直接copy吗? (⊙o⊙)…

/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<fstream>
using namespace std;
#define HaHa freopen("in.txt","r",stdin)
#define MAXN 20000
int n_poi,n_edg,cnt;
vector<int> vec[MAXN+5];
set<int> ans;
struct Fans{int u,v;}fans[MAXN+20000];int n_fans;
int dfn[MAXN+5],fa[MAXN+5],low[MAXN+5];
bool edg_or_poi;
bool cmp(Fans A,Fans B)
{
    if(A.u-B.u)
        return A.u<B.u;
    else
        return A.v<B.v;
}
ofstream O("haha.txt");
void DFS(int u)
{
    dfn[u]=low[u]=++cnt;
    int siz=vec[u].size(),n_chl=0;
    for(int i=0;i<siz;i++)
    {
        int v=vec[u][i];
        if(!dfn[v])
        {
            n_chl++;
            fa[v]=u;
            DFS(v);
            low[u]=min(low[u],low[v]);
            if(fa[u]==-1)
            {
                if(n_chl>=2)
                    ans.insert(u);
            }
            else
            {
                if(edg_or_poi)
                {
                    if(low[v]>dfn[u])
                        fans[n_fans].u=min(u,v),fans[n_fans++].v=max(u,v);
                }
                else
                {
                    if(low[v]>=dfn[u])
                        ans.insert(u);
                }
            }
        }
        else
            if(v!=fa[u])
                low[u]=min(low[u],dfn[v]),O<<0;
    }

}
int main()
{
    HaHa;
    scanf("%d%d",&n_poi,&n_edg);
    for(int i=0;i<n_edg;i++)
    {
        int A,B;scanf("%d%d",&A,&B);
        vec[A].push_back(B);
        vec[B].push_back(A);
    }
    fa[1]=-1;
    DFS(1);
    if(ans.empty()) puts("Null");
    for(set<int>::iterator it=ans.begin();it!=ans.end();it++)
        it==ans.begin()? true:putchar(' '),printf("%d",*it);
    memset(dfn,0,sizeof dfn);
    memset(fa,0,sizeof fa);
    memset(low,0,sizeof low);
    puts("");
    edg_or_poi=true;
    DFS(1);
    sort(fans,fans+n_fans,cmp);
    for(int i=1;i<n_fans;i++)
        printf("%d %d\n",fans[i].u,fans[i].v);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值