• Tarjan-有向图


    (我到底是咕了多少知识点啊)

    在有向图中tarjan主要用来求强连通分量并缩点

    一、定义

    强连通:如果两个顶点可以相互通达,则称两个顶点 强连通

    强连通分量:如果有向图G的每两个顶点都 强连通,称G是一个强连通图。非 强连通图有向图的极大强连通子图,称为强连通分量

    树枝边:原图上的边中,在DFS树上由父亲指向儿子的叫树枝边

    前向边:由父亲指向儿子以下的其他后代的叫前向边

    后向边:由后代指向祖先的边叫后向边

    横叉边:一个子树中的点指向另一个子树中的点的边叫横叉边

    二、tarjan

    用来求强联通分量

    基于dfs

    每个强连通分量为搜索树中的一颗子树

    三、算法

    首先要引入两个非常重要的数组:dfn[ ] 和 low[ ]

    dfn[ ] :就是一个时间戳(被dfs到的次序)。

    low [ ] : 该子树中,且仍在栈中的最小时间戳(即可以到达的图中dfn值最小的点得dfn值)

    这个图不一定是一个连通图,所以跑tarjan的时候要枚举每个点

    若dfn[ ] == 0,进行深搜

    然后对于搜到的点寻找与其有边相连的点,判断这些点是否已经被搜索过,若没有,则进行搜索。若该点已经入栈,说明形成了环,则更新low.

    在不断深搜的过程中如果没有路可走了(出边遍历完了),那么就进行回溯,回溯时不断比较low[ ],去最小的low值。如果dfn[x]==low[x]则x可以看作是某一强连通分量子树的根,也说明找到了一个强连通分量,然后对栈进行弹出操作,直到x被弹出。

    洛谷缩点板子题

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    inline int read()
    {
        int sum = 0,p = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-')
                p = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            (sum *= 10) += ch - '0';
            ch = getchar();
        }
        return sum * p;
    }
    
    const int maxn = 1e5+1e4,maxm = 1e5 + 1e4;
    int vis[maxn],dfn[maxn],low[maxn],tim;
    int ncnt,bel[maxn],sum[maxn],f[maxn];
    int cnt,head[maxn],nxt[maxm],to[maxm];
    int sta[maxn],top;
    int n,m,val[maxn],x[maxm],y[maxm],ans;
    
    void add(int a,int b)
    {
        nxt[++cnt] = head[a];
        to[cnt] = b;
        head[a] = cnt;
    }
    
    void tarjan(int x)
    {
        dfn[x] = low[x] = ++tim;
        sta[++top] = x;
        vis[x] = 1;
        for(int i = head[x];i;i = nxt[i])
        {
            int v = to[i];
            if(!dfn[v])
            {
                tarjan(v);
                low[x] = min(low[x],low[v]);
            }
            else if(vis[v])
                low[x] = min(low[x],dfn[v]);
        }
        if(low[x] == dfn[x])
        {
            ncnt++;
            while(sta[top+1] != x)
            {
                bel[sta[top]] = ncnt;
                sum[ncnt] += val[sta[top]];
                vis[sta[top--]] = 0;
            }
        }
    }
    
    void search(int x)
    {
        if(f[x]) 
            return;
        f[x] = sum[x];
        int maxsum = 0;
        for(int i = head[x];i;i = nxt[i])
        {
            if(!f[to[i]])
                search(to[i]);
            maxsum = max(maxsum,f[to[i]]);
        }
        f[x] += maxsum;
    }
    
    int main()
    {
        n = read();
        m = read();
        for(int i = 1;i <= n;i++)
            val[i] = read();
        for(int i = 1;i <= m;i++)
        {
            x[i] = read();
            y[i] = read();
            add(x[i],y[i]);
        }
        for(int i = 1;i <= n;i++)
            if(!dfn[i])
                tarjan(i);
        memset(head,0,sizeof(head));
        memset(nxt,0,sizeof(nxt));
        memset(to,0,sizeof(to));
        cnt = 0;
        for(int i = 1;i <= m;i++)
        {
            if(bel[x[i]] != bel[y[i]])
                add(bel[x[i]],bel[y[i]]);
        }
        for(int i = 1;i <= ncnt;i++)
            if(!f[i])
            {
                search(i);
                ans = max(ans,f[i]);
            }
        printf("%d",ans);
        return 0;
    } 
  • 相关阅读:
    python脚本2_输入2个数比较大小后从小到大升序打印
    python脚本1_给一个半径求圆的面积和周长
    配置双机互信
    如何在 CentOS7 中安装 Nodejs
    Git 服务器搭建
    docker安装脚本
    CentOS7下安装Docker-Compose
    Linux 文件锁
    6 系统数据文件和信息
    bash脚本编程之二 字符串测试及for循环
  • 原文地址:https://www.cnblogs.com/darlingroot/p/11220504.html
Copyright © 2020-2023  润新知