• tarjan算法板子


    无向图

    • 概念

      • 时间戳
        (dfn[x]),在深度优先遍历中,按照每个节点第一次被访问的顺序,依次做整数标记

      • 追溯值
        (low[x]),通过非搜索边能到达的最小时间戳

    • 割边判定法则

      • 无向边((x,y))是割边/桥,当且仅当存在x的一个子节点满足(dfn[x] < low[y])
        删除无向边((x,y))后,图断开成两个部分

      • 板子

    int dfn[N], low[N], dfcnt;
    bool g[M];
    void tarjan(int x, int ei) {
        dfn[x] = low[x] = ++dfcnt;
        for(int i = head[x]; i; i = e[i].next) {
            int y = e[i].t;
            if (!dfn[y]) {
                tarjan(y, i);
                low[x] = min(low[x], low[y]);
                if (dfn[x] < low[y]) g[i] = g[i^1] = 1;
            }
            else if (i != (ei^1)) low[x] = min(low[x], dfn[y]);
        }
    }
    
    • 割点判定法则

      • 若x不是根节点,则x是割点当且仅当存在一个子节点y满足(dfn[x]leq low[y])
        若x是根节点,则x是割点当且仅当存在至少两个子节点(y_1,y_2)满足上条件

      • 板子

    int dfn[N], low[N], dfcnt, rt;
    bool g[N];
    void tarjan(int x) {
        dfn[x] = low[x] = ++dfcnt;
        int son = 0;
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].t;
            if (!dfn[y]) {
                tarjan(y);
                low[x] = min(low[x], low[y]);
                if (dfn[x] <= low[y]) {
                    son++;
                    if (x != rt || son > 1) g[x] = 1;
                }
            }
            else low[x] = min(low[x], dfn[y]);
        }
    }
    
    • 点双联通分量

      • 对于,每个点双中来说,图里是不存在割点的

      • 板子

    int dfn[N], low[N], dfcnt, sta[N], top, cnt;
    vector<int> dcc[N];
    bool  g[N];
    void tarjan(int x, int rt) {
        dfn[x] = low[x] = ++dfcnt;
        sta[++top] = x;
        int son = 0;
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].t;
            if (!dfn[y]) {
                tarjan(y); 
                low[x] = min(low[x], low[y]);
                if (dfn[x] <= low[y]) {
                    son++;
                    if (x != rt || son > 1)  g[x] = 1;
                    dcc[++cnt].clear();
                    while (1) {
                        int z = sta[top--];
                        dcc[cnt].push_back(z);
                        if (y == z) break;
                    }
                    dcc[cnt].push_back(x);
                }
            }
            else low[x] = min(low[x], dfn[y]);
        }
    }
    
    • 边双联通分量

      • 对于一个边双,任意两个点都有两条不重合的路径

      • 板子

    int dfn[N], low[N], dfcnt;
    bool g[M];
    void tarjan(int x, int ei) {
        dfn[x] = low[x] = ++dfcnt;
        for(int i = head[x]; i; i = e[i].next) {
            int y = e[i].t;
            if (!dfn[y]) {
                tarjan(y, i);
                low[x] = min(low[x], low[y]);
                if (dfn[x] < low[y]) g[i] = g[i^1] = 1;
            }
            else if (i != (ei^1)) low[x] = min(low[x], dfn[y]);
        }
    }
    int n, m, d[N], b[N], cnt, ans;
    void dfs(int x) {
        b[x] = cnt;
        for(int i = head[x]; i; i = e[i].next) {
            int y = e[i].t;
            if (b[y] || g[i]) continue;
            dfs(y);
        }
    }
    int main() {
        //~~~
        for(int i = 1; i <= n; i++)
            if (!dfn[i]) tarjan(i, 0);
        for(int i = 1; i <= n; i++)
            if (!b[i]) cnt++, dfs(i);
        //~~~
        return 0;
    }
    

    有向图

    • 有向图的强联通分量

      • 在一个强联通分量中,存在x到y的路径,就存在y到x的路径

      • 板子

    void tarjan(int x) {
        dfn[x] = low[x] = ++dfcnt;
        s[++top] = x;
        for(int i = head[x]; i; i = e[i].next) {
            int y = e[i].t;
            if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
            else if (!b[y]) low[x] = min(low[x], dfn[y]);
        }
        if (dfn[x] == low[x]) {
            cnt++;
            while(1) {
                int y = s[top--];
                b[y] = cnt;
                size[cnt]++;
                if (x == y) break;
            }
        }
    }
    

    例题

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N = 1e4+5, M = 1e5+5;
    struct side { int t, next; } e[M][2];
    int head[N][2], tot[2];
    void add(int x, int y, int k) {
        e[++tot[k]][k].next = head[x][k]; 
        head[x][k] = tot[k];
        e[tot[k]][k].t = y;
    }
    int n, m, w[N], r[N], d[N], ans;
    int dfn[N], low[N], dfcnt, sta[N], top, cnt, bel[N], sum[N];
    void tarjan(int x) {
        dfn[x] = low[x] = ++dfcnt;
        sta[++top] = x;
        for (int i = head[x][0]; i; i = e[i][0].next) {
            int y = e[i][0].t;
            if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
            else if (!bel[y]) low[x] = min(low[x], dfn[y]);
        }
        if (dfn[x] == low[x]) {
            cnt++;
            while (1) {
                int y = sta[top--];
                bel[y] = cnt;
                sum[cnt] += w[y];
                if (x == y) break;
            }
        }
    }
    queue<int> q;
    int tuopu() {
        for (int i = 1; i <= cnt; i++)
            if (!r[i]) q.push(i), d[i] = sum[i];
        while (!q.empty()) {
            int x = q.front(); q.pop();
            for (int i = head[x][1]; i; i = e[i][1].next) {
                int y = e[i][1].t;
                d[y] = max(d[y], d[x] + sum[y]);
                if (--r[y] == 0) q.push(y);
            }
        }
        for (int i = 1; i <= cnt; i++)
            ans = max(ans, d[i]);
        return ans;
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            scanf("%d", &w[i]);
        for (int i = 1; i <= m; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            add(x, y, 0);
        }
        for (int i = 1; i <= n; i++)
            if (!dfn[i]) tarjan(i);
        for (int x = 1; x <= n; x++)
            for (int i = head[x][0]; i; i = e[i][0].next) {
                int y = e[i][0].t;
                if (bel[x] != bel[y]) 
                    r[bel[y]]++, add(bel[x], bel[y], 1);
            }
        printf("%d
    ", tuopu());
        return 0;
    }
    
  • 相关阅读:
    大数据开发,Hadoop Spark太重?你试试esProc SPL
    不需要服务器,教你仅用30行代码搞定实时健康码识别
    学python,怎么能不学习scrapy呢!
    如何面向对象编程?程序员:我也要先有“对象”啊
    5种kafka消费端性能优化方法
    内存问题难定位,那是因为你没用ASAN
    基于DMS的数仓智能运维服务,知多少?
    云图说丨初识华为云微服务引擎CSE
    CC2530_ZigBee+华为云IOT:设计一套属于自己的冷链采集系统
    nacos config 及 自动刷新注解
  • 原文地址:https://www.cnblogs.com/Z8875/p/13187415.html
Copyright © 2020-2023  润新知