(1668): 割点和割边

本文介绍如何求解无向连通图中的割点与割边数量,通过深度优先搜索(DFS)算法,详细解释了割点与割边的概念及其判断条件。

这就是一道裸题啊23333

 

时间限制: 1 Sec  内存限制: 64 MB

题目描述

给出一个无向连通图, 求出所有割点与割边的数量。

输入

第1行: 2个整数N,M (1 <= N <= 5,000,N-1 <= M <= 10,000),分别表示顶点数和边数 
接下来M行,每行2个整数,表示图中的一条边。

输出

第1行:1个整数,表示割点数 
第2行:1个整数,表示割边数

样例输入

 

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

11 13
1 2
1 4
1 5
1 6
2 11
2 3
4 3
4 9
5 8
5 7
6 7
7 10
11 3

样例输出

4
3

 

 

在这里顺便讲一讲割点和割边是什么和怎么求的好了...

 

 

先介绍几个概念:

dfn:在DFS时,用dfn[u]表示编号为u的节点在DFS过程中的访问序号(也叫做开始时间)。dfn[u]称为顶点u的深度优先数。显然,越先访问的节点dfn值越小。

树边/反向边(返祖边):

 

通常,当我们到达某个顶点u时,会有两种情况出现:

    (1)如果存在与u关联的未被检查的边,那么我们就选择这样的边(u,v),并定向为从u到v。这时,边(u,v)就说成是被检查过。现在有两种情形都需要考虑:

情形1 如果顶点v原来未被访问过,那么我们就经过边(u,v)去访问v,并从v继续搜索。在这种情况下,(u,v)就是一条树边,且FATHER[v] = u。

情形2 如果顶点v原来已经被访问过,那么我们就继续选择一条与u关联的其它未检查过的边。在这种情况下,边(u, v)就称为一条反向边。

   (2)如果与u关联的所有边都已检查过,那么我们返回到u的父亲,并继续从父亲结点的未检查过的邻接边搜索。顶点u这时就说成是完全扫描过。当搜索返回到起始顶点,并且所有的边都已经被检查过了,DFS就终止。

 

 

 

low:

 

我们通过DFS把无向图定向成有向图,定义每个顶点的low参数,low[u]表示沿u出发的有向轨能够到达的点v中,dfn[u]的最小值。(经过返祖边后则停止)

low[u]定义为u或者u的子树中能够通过非父子边追溯到的节点的DFS最早开始时间

low[u]的定义提供了如下的计算步骤:

1. 当u是DFS期间首次访问时,则low[u] = dfn[u]

2. 当与u关联的一条反向边(u,v)被检查时,low[u]= min{low[u], dfn[v]}

3. 当DFS在u的一个儿子v完全扫描后返回到u时,low[u] = min{low[u], low[v]}

 

 

 

 

 

割点:去掉后使图不连通的点。

割边:去掉后使图不连通的边。

 

求法:(详见后面代码)

割点:low[v]>=dfn[u](v最远也只能到它爸爸)

割边:low[v]>dfn[u](v已经回不到它的祖先了)

 

 

所以就直接上一段乱搞的AC代码吧..._(:з」∠)_

 

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,s[5005],dfn[5005],low[5005],gd,gb,t,rts,ctp[5005];
struct nde
{
    int x,y;
}a[200005];
bool cmp(nde p,nde q){return p.x!=q.x?(p.x<q.x):(p.y<q.y);}
void dfs(int u,int f)
{
    int i;
    dfn[u]=low[u]=++t;
    for(i=s[u];a[i].x==u;i++) 
    {
        if(!dfn[a[i].y])
        {
            dfs(a[i].y,u);
            low[u]=min(low[u],low[a[i].y]);
            if(low[a[i].y]>dfn[u]) gb++;
            if(low[a[i].y]>=dfn[u]&&!ctp[u]) //避免重复计数
            {
                if(f&&!ctp[u]) {gd++;ctp[u]=1;}
                else rts++;
                if(rts>1&&!f&&!ctp[u]) {gd++;ctp[u]=1;}
            }
        }
        else if(a[i].y!=f) low[u]=min(low[u],dfn[a[i].y]);
    }
}
int main()
{
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m*2;i+=2) {scanf("%d%d",&a[i].x,&a[i].y);a[i+1].x=a[i].y;a[i+1].y=a[i].x;}
    sort(a+1,a+m*2+1,cmp);
    for(i=1;i<=m*2;i++) if(a[i].x!=a[i-1].x) s[a[i].x]=i;
    dfs(1,0);
    printf("%d\n%d\n",gd,gb);
}

 

 

 

 

 

 

沦为高一狗之后就开始潜水了..23333偶尔冒个泡

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值