zoukankan      html  css  js  c++  java
  • tarjan算法板子

    无向图

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

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

    割边判定法则

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

    Code

    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)满足上条件

    Code

    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]);
        }
    }
    

    点双联通分量

    • 对于,每个点双中来说,图里是不存在割点的
      于是,这里可以就可以将图转成一颗圆方树了。

    Code

    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]);
        }
    }
    

    边双联通分量

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

    Code

    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的路径

    Code

    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;
            }
        }
    }
    

    例题

    Code

    #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;
    }
    
  • 相关阅读:
    OPPO R9sPlus MIFlash线刷TWRP Recovery ROOT详细教程
    OPPO R11 R11plus系列 解锁BootLoader ROOT Xposed 你的手机你做主
    努比亚(nubia) M2青春版 NX573J 解锁BootLoader 并进入临时recovery ROOT
    华为 荣耀 等手机解锁BootLoader
    青橙 M4 解锁BootLoader 并刷入recovery ROOT
    程序员修炼之道阅读笔03
    冲刺8
    典型用户模板分析
    学习进度八
    冲刺7
  • 原文地址:https://www.cnblogs.com/shawk/p/13187415.html
Copyright © 2011-2022 走看看