知识点 - 割点与割边

知识点 - 割点与割边

概念(一)割点

针对无向连通图,若删除一个点后使得该图不连通,则该点是割点。

注意:一个图中可能有多个割点

复杂度:

O ( N + M ) O(N+M) O(N+M)

实现

我们在遍历所有点时会遇到割点(废话),主要是如何认定一个点是割点。假设访问到了k点,如果在没有访问过的点中,至少有一个点在不经过k点的情况下,无法回到已访问过的点,则k点是割点。(因为该图删除点k后不连通了)

算法核心: 如何判断未被访问过的点u在不经过点k的情况下能否返回任何一个已访问过的点。

从树的角度来看,k是u的父亲,u是k的儿子,判断u能否不经过k而回到它的所有祖先。

我们用数组low来表示每个点在不经过父节点的前提下,能返回的最早的时间戳。

#include <bits/stdc++.h>
using namespace std;
int n,m,e[1005][1005],root,num[1005],low[1005],flag[1005],index;

void dfs(int cur,int dad) {
    int i,child=0;
    index++;
    num[cur]=index;
    low[cur]=index;
    for(i=1; i<=n; i++) {
        if(e[cur][i]==1) {
            if(num[i]==0) {
                child++;
                dfs(i,cur);
                low[cur]=min(low[cur],low[i]);
                if(cur!=root && low[i]>=num[cur]) {
                    flag[cur]=1;
                } else if(cur==root && child==2) {
                    flag[cur]=1;
                }
            } else if(low[i]!=dad) {
                low[cur]=min(low[cur],num[i]);
            }
        }
    }
}
int main() {
    int i,a,b,c;
    scanf("%d%d",&n,&m);
    memset(e,0,sizeof(e));
    for(i=1; i<=m; i++) {
        scanf("%d%d",&a,&b);
        e[a][b]=1;
        e[b][a]=1;
    }
    root=1;
    dfs(1,root);
    return 0;
}

概念(二)割边

割边也成为桥,与割点类似

针对无向连通图,若删除一条边后使得该图不连通,则该边是割边。

同样的,一个图中可能有多条割边

割边的求法也与割点类似,只需将

if(low[i]>=num[cur])

改为:

if(low[i]>num[cur])

就行了,即该点到除了达不了它的任何祖先,也无法到达它的父亲。
好了,呈上代码,请注意输出部分:

#include<bits/stdc++.h>
using namespace std;
int n,m,e[1005][1005],num[1005],low[1005];
int root,index;

void dfs(int cur,int dad) { //待判断顶点和它的父节点
    int i;
    index++;//index储存的是时间戳
    num[cur]=low[cur]=index;
    for(i=1; i<=n; i++) {
        if(e[cur][i]==1) {
            if(num[i]==0) { //i是cur的儿子
                dfs(i,cur);//继续遍历
                low[cur]=min(low[cur],low[i]);//更新low的值
                if(low[i]>num[cur])
                    printf("%d-%d\n",cur,i);
            } else if(i!=dad) //i是cur的祖先,更新low
                low[cur]=min(low[cur],num[i]);
        }
    }
}
int main() {
    int i,a,b;
    scanf("%d%d",&n,&m);
    for(i=1; i<=m; i++) { //注意是无向图哦
        scanf("%d%d",&a,&b);
        e[a][b]=1;
        e[b][a]=1;
    }
    root=1;
    dfs(1,root);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值