tarjan求割顶和桥 hihoCoder1183 连通性一·割边与割点

终于把tarjan算法理解了


整个tarjan算法有两个非常重要的数组,一个是Low一个是DFN

DFN数组是个时间戳,说白了就是访问那个节点的时间,也就是第几次调用DFS这个函数


Low是u的子节点能通过反向边到达的节点DFN的最小值,初始值为DFN[u]

那么如何判断是否是割顶和割边呢


对于上面这个建图,假如我们最开始的根节点是1,然后会得到这样的数组

id  1 2 3 4 5 6
dfn 1 2 3 4 5 6
low 1 1 1 4 4 4

对于割顶,个人认为可以分成3种情况

第一种情况,是对我们DFS树的根节点,对于我刚刚说的,我们一开始访问的是1,那么 1就是根节点,

这个点是不是割顶呢?这要看它儿子的数量,如果>=2,那么把这个点拆了,必然会有增加的连通分量。如果等于1,那么拆了也不会有增加的连通分量,所以儿子数量等于1,就不是割顶,大于1就是割顶


第二种情况,对于某个点u,设其儿子为v,如果存在一个v,使得low[v] >dfn[u] ,在上面这个样例里面,有存在的情况,u=3,v=4就是满足这种情况的

第三种情况,对于某个点u,设其儿子为v,如果存在一个v,使得low[v]==dfn[u,在上面这个样例里面,有存在的情况,u=4,v=5就是满足这种情况的


对于桥,就是割顶的第二种情况

对于某个点u,设其儿子为v,如果存在一个v,使得low[v] >dfn[u] ,在上面这个样例里面,有存在的情况,u=3,v=4就是满足这种情况的,那么说明v怎么也回不到u的父节点去,所以此时u-v这条边就是桥


#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<functional>
#include<algorithm>


using namespace std;
typedef long long LL;
typedef pair<int, int> PII;


const int MX = 2e5 + 5;
const int INF = 0x3f3f3f3f;


int rear;
int Head[MX], Next[MX];
int Low[MX], DFN[MX], dfs_clock;
int cut[MX];


struct Edge {
    int u, v, sign;
} E[MX];


void edge_init() {
    rear = 0;
    memset(Head, -1, sizeof(Head));
    memset(Next, -1, sizeof(Next));
}


void edge_add(int u, int v) {
    E[rear].u = u;
    E[rear].v = v;
    E[rear].sign = false;
    Next[rear] = Head[u];
    Head[u] = rear++;
}


int tarjan(int u, int from) {
    Low[u] = DFN[u] = ++dfs_clock;


    int child = 0;
    for(int id = Head[u]; ~id; id = Next[id]) {
        int v = E[id].v;


        if(!DFN[v]) {
            int lowv = tarjan(v, u);
            Low[u] = min(Low[u], lowv);


            if(lowv >= DFN[u]) {
                cut[u] = 1;
            }
            if(lowv > DFN[u]) {
                E[id].sign = 1;
                E[id ^ 1].sign = 1;
            }


            child++;
        } else if(v != from) {
            Low[u] = min(Low[u], DFN[v]);
        }
    }


    if(from == -1 && child == 1) cut[u] = 0;
    return Low[u];
}


int main() {
    int n, m;
    scanf("%d%d", &n, &m);


    dfs_clock = 0;
    edge_init();
    memset(DFN, 0, sizeof(DFN));
    memset(cut, 0, sizeof(cut));


    for(int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        edge_add(u, v);
        edge_add(v, u);
    }


    tarjan(1, -1);//如果已经确定是连通图,就这样写
    /*
    否则要这样
    for(int i = 1; i <= n; i++) {
        if(!DFN[i]) tarjan(1, -1);
    }
    */
    int cnt = 0, first = true;
    for(int i = 1; i <= n; i++) {
        if(cut[i]) {
            cnt++;


            if(first) first = false;
            else printf(" ");
            printf("%d", i);
        }
    }
    printf("%s\n", cnt ? "" : "Null");


    vector<PII>ans;
    for(int i = 0; i < rear; i++) {
        if(E[i].sign && E[i].u < E[i].v) {
            ans.push_back(PII(E[i].u, E[i].v));
        }
    }
    sort(ans.begin(), ans.end());
    for(int i = 0; i < ans.size(); i++) {
        printf("%d %d\n", ans[i].first, ans[i].second);
    }
    return 0;
}


你可能感兴趣的:(tarjan求割顶和桥 hihoCoder1183 连通性一·割边与割点)