Skip to content

Latest commit

 

History

History
119 lines (88 loc) · 3.84 KB

割点.md

File metadata and controls

119 lines (88 loc) · 3.84 KB

原理

一.基本概念

​ 1.桥:若无向连通图的边割集中只有一条边,则称这条边为割边或者桥 (离散书上给出的定义。。

​ 通俗的来说就是无向连通图中的某条边,删除后得到的新图联通分支至少为2(即不连通;

​ 2.割点:若无向连通图的点割集中只有一个点,则称这个点为割点或者关节点 ;

​ 通俗的来说就是无向连通图中的某条边,删除后得到的新图连通分支至少为2;

二:tarjan算法求割点和桥

​ 1.割点:1)当前节点为树根的时候,条件是“要有多余一棵子树”;

     如果这有一颗子树,去掉这个点也没有影响,如果有两颗子树,去掉这点,两颗子树就不连通了;

​ 2)当前节点u不是树根的时候,条件是“low[v]>=dfn[u]”,也就是在u之后遍历的点,能够向上翻,

      最多到u,如果能翻到u的上方,那就有环了,去掉u之后,图仍然连通。

​ 2.桥:若一条无向边(u,v)是桥,

​ 1)当且仅当无向边(u,v)是树枝边,需要满足dfn(u)<low(v),即v向上翻不到u及其以上的点,

      那么u--v之间一定能够有1条或者多条边不能删去, 因为他们之间有部分无环,是桥,

      如果v能上翻到u那么u--v就是一个环,删除其中一条路径后,仍然是连通的。

​ 3.注意点:1)求桥的时候:因为边是无方向的,所以父亲孩子节点的关系需要自己规定一下,

​ 在tarjan的过程中if(v不是u的父节点) low[u]=min(low[u],dfn[v]);

​ 因为如果v是u的父亲,那么这条无向边就被误认为是环了。

​ 2)找桥的时候:注意看看有没有重边,有重边的边一定不是桥,也要避免误判。

​ 4.也可以先进行tarjan(),求出每一个点的dfn和low,并记录dfs过程中的每个点的父节点,

   遍历所有点的low,dfn来寻找桥和割点

代码

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;

const int MAXN=1e5+10;
vector<int> mp[MAXN];
bool is_cut[MAXN];
int n, m, count=0;
int low[MAXN], dfn[MAXN], pre[MAXN];//pre[u]记录u的父亲节点编号
//dfn[u]记录节点u在DFS过程中被遍历到的次序号,low[u]记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小

void tarjan(int u, int fu){
    pre[u]=fu;//记录当前u的父亲节点
    dfn[u]=low[u]=count++;
    for(int i=0; i<mp[u].size(); i++){
        int v=mp[u][i];
        if(dfn[v]==-1){
            tarjan(v, u);
            low[u]=min(low[u], low[v]);
        }else if(fu!=v){//如果v是u的父亲的话,即有重边,那么不可能是桥
            low[u]=min(low[u], dfn[v]);
        }
    }
}

void solve(void){
    int rootson=0;
    tarjan(1, 0);
    for(int i=2; i<=n; i++){
        int v=pre[i];
        if(v==1){
            rootson++;//统计根节点的子树个数,若其不小于2,即为割点
        }else if(low[i]>=dfn[v]){
            is_cut[v]=true;
        }
    }
    if(rootson>1) is_cut[1]=true;
    puts("割点为:");
    for(int i=1; i<=n; i++){//输出割点
        if(is_cut[i]){
            printf("%d ", i);
        }
    }
    puts("\n桥为:");
    for(int i=1; i<=n; i++){
        int v=pre[i];
        if(v>0&&low[i]>dfn[v]){
            printf("%d %d\n", v, i);
        }
    }
    puts("");
}

int main(void){
    scanf("%d%d", &n, &m);
    for(int i=0; i<m; i++){
        int x, y;
        scanf("%d%d", &x, &y);
        mp[x].push_back(y);
        mp[y].push_back(x);
    }
    memset(dfn, -1, sizeof(dfn));
    memset(low, -1, sizeof(low));
    solve();
    return 0;
}