图论-割点与割边(已被优化)

这是摘自算法书上的一篇 Tarjan 求割点算法

dfn[i] 代表时间戳数组
back[i] 代表该点 不依靠祖先节点 能回到的最远的祖先节点

采用链式前向星建图,结果存储在 iscut[ ] 数组中


点击查看代码
int head[N], cnt = 0;
struct Edge{
    int from, to, nxt;
}e[N << 1];
void add(int u, int v){
    e[++cnt].from = u;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

int dfn[N], back[N], tim; // dfn[i] 时间戳  back[i] 回退到祖先
bool iscut[N]; // 结果数组

void dfs(int u, int fa){
    dfn[u] = back[u] = ++tim;
    int child = 0;
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(!dfn[v]){
            // v 没访问过
            child++;
            dfs(v, u);
            back[u] = min(back[u], back[v]);
            if(back[v] >= dfn[u] && u != 1)
                iscut[u] = true;
        }
        else if(dfn[v] < dfn[u] && v != fa){
            back[u] = min(back[u], dfn[v]); // 注意不是  back[v]
        }
    }
    if(u == 1 && child >= 2){
        iscut[u] = true;
    }
}

后来敲的时候,有一行代码为 back[u] = min(back[u], dfn[v]);容易误写为back[u] = min(back[u], back[v]
此时就要重视 back[ ] 表示 不依靠祖先节点

当然,这个稍微改动下,就是割边


点击查看代码
int head[N], cnt = 0;
struct Edge{
    int from, to, nxt;
}e[N << 1];
void add(int u, int v){
    e[++cnt].from = u;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

int dfn[N], back[N], tim; // dfs[i] 时间戳  back[i] 回退到祖先
bool iscut[N << 1]; // 结果数组

void dfs(int u, int fa){
    dfn[u] = back[u] = ++tim;
    int child = 0;
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(!dfn[v]){
            // v 没访问过
            child++;
            dfs(v, u);
            back[u] = min(back[u], back[v]);
            if(back[v] > dfn[u] && u != 1)
                iscut[i] = true;  // 边为
        }
        else if(dfn[v] < dfn[u] && v != fa){
            back[u] = min(back[u], dfn[v]); // 注意不是  back[v]
        }
    }
    if(u == 1 && child >= 2){
        for(int i = head[u]; i != 0; i = e[i].nxt){
            iscut[i] = true;
        }
    }
}

至于这份割边代码,iscut数组记录的倒是有些奇怪,但也能输出边的信息


2024/5/10:补一张情况说明,借用了洛谷名为 wind_seeker 的一张图
image

对此,我做出求割点(其实笔者感觉应该称为无向图的性质)的 back 数组概括:

本环境下,back[i]仅取决于自己与子节点(所有时间戳在自己之后且能联通的)能连接到祖先的能力
而与祖先节点连接到更祖先节点的能力无关

(我父亲厉害与我无关,但我儿子厉害与我有关)


2024.5.22 经过几天的点双,边双的思考后,优化了割点与割边的代码,使我更深入的理解了 Tarjan 算法,细节过多,新开两篇详细讲述

posted @ 2024-05-09 22:02  9102700  阅读(24)  评论(0)    收藏  举报