• tarjan学习(复习)笔记(持续更新)(各类找环模板)


    题目背景

    缩点+DP

    题目描述

    给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

    允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

    输入输出格式

    输入格式:

    第一行,n,m

    第二行,n个整数,依次代表点权

    第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

    输出格式:

    共一行,最大的点权之和。

    输入输出样例

    输入样例#1: 复制
    2 2
    1 1
    1 2
    2 1
    输出样例#1: 复制
    2

    说明

    n<=10^4,m<=10^5,0<=点权<=1000

    算法:Tarjan缩点+DAGdp、

    基本可以算是套路了吧,先缩点,然后tpsort,跑dp,是不是可以解决不少图论题目呢

    思路:

    两个数组,dfn,low。

    dfn:dfs序(时间戳)

    low:以u为根的子树里dfn最小的那个点(它的最早祖先)

    附属数组:

    st:模拟栈

    co:重建图的联通快(点)

    维护这两个数组,当dfn[u]=low[u]时判定为强连通分量(环)

    为什么呢?

    当一个点它的最老祖先等于它自己的时候,这就是一个环啊

    了解四种边:

    树枝边:遍历路径

    前向边:爹——>儿

    后向边:儿——>爹

    横插边:从这个子树插到另外一个搜索子树的边

    下面介绍怎么维护low

    如果(u,v)是树枝边,一切好说,直接比较low[u]和low[v]的最小值即可,因为v是u的儿子,直接比较它们最早祖先的大小。

    如果(u,v)是后向边或者横插边,就需要比较lou[u]和dfn[v]的最小值。

    为什么?

    后向边相对好理解,从这个点可以回溯到它的祖先,我们需要比较它儿子们的时间戳最小值和它祖先的时间戳 的最小值。

    若之前搜到过u的祖先,那么它祖先的dfn一定是小的,但是我能从它的耳孙之间找到它的身影(自交?回交?)!

    这说明什么?强连通分量!

    但是,不要着急,我们需要找到强连通分量的根。所以我们需要比较一个极小值。

    解释通了,那么横插边也是同理。

    当dfn=low时:

    也就是说它的子孙的最高祖先就是子孙本身时。

    dfn时间戳正好是它子树节点的low的最小值。因为dfn值具有不重复性,所以可以断定,以它为根的子树的所有点都是一个强连通分量。

    所以,可以很好地判断图的环。

    注意:因为图可能不连通,所以要多次跑tarjan。

    时间复杂度:由于每个点只遍历了一次,每条边也只遍历了一次,所以O(N+M)(不是spfa那么不靠谱,人家就是N+M)

    给出缩点板子的代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100005;
    struct node
    {
        int next,to;
    }e[maxn];
    int head[maxn],cnt,sum[maxn],a[maxn];
    int n,m,ru[maxn];
    inline void addedge(int from,int to)
    {
        e[++cnt].next=head[from];
        e[cnt].to=to;
        head[from]=cnt;
    }
    int dep,top;
    int dfn[maxn],low[maxn],vis[maxn],co[maxn],st[maxn];
    void tarjan(int u)
    {
        dfn[u]=low[u]=++dep;
        vis[u]=1;
        st[++top]=u;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[v],low[u]);
            }
            else if(vis[v])
            {
                low[u]=min(low[u],dfn[v]);
            }
        }
        if(dfn[u]==low[u])//退栈,把强连通分量薅出来
        {
            int t;
            do
            {
                t=st[top--];
                sum[u]+=a[t];
                co[t]=u;
                vis[t]=0;
            }while(t!=u);
        }
    }
    int dp[maxn];
    queue < int > q;
    void tpsort()
    {
        for(int i=1;i<=n;i++)
        {
            if(ru[i]==0&&co[i]==i)
            q.push(i);
            dp[co[i]]=sum[co[i]];
        }
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int i=head[u];i;i=e[i].next)
            {
                int v=e[i].to;
                dp[v]=max(dp[u]+sum[co[v]],dp[v]);
                if(!(--ru[v]))
                {
                    q.push(v);
                }
            }
        }
    }
        
    pair < int , int > g[maxn];
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            addedge(x,y);
            g[i].first=x;
            g[i].second=y;
        }
        for(int i=1;i<=n;i++)
        {
            if(!dfn[i])
            tarjan(i);
        }
        memset(e,0,sizeof(e));
        memset(head,0,sizeof(head));
        cnt=0;
        for(int i=1;i<=m;i++)
        {
            int x=g[i].first;
            int y=g[i].second;
            if(co[x]!=co[y])
            {
                addedge(co[x],co[y]);
                ru[co[y]]++;
            }
        }
        tpsort();
        int ans=-1;
        for(int i=1;i<=n;i++)
        {
            ans=max(ans,dp[i]);
        }
        printf("%d",ans);
        return 0;
    }

    无向图:

    void tarjan(int u)
    {
        dfn[u]=low[u]=++tot;
        st[++top]=u;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(!vis[i])
            {
                vis[i]=vis[i^1]=1;
                if(dfn[v]==0)
                {
                    tarjan(v);
                    low[u]=min(low[u],low[v]);
                }
                else
                {
                    low[u]=min(low[u],dfn[v]);
                }
            }
        }
        if(dfn[u]==low[u])
        {
            color[u]=++col;
            while(st[top]!=u)
            color[st[top]]=col,top--;
            top--;
        }        
    }

    (完)

  • 相关阅读:
    Kettle学习(2)
    kettle学习(1)
    Quartz在Spring中的使用
    JVM垃圾收集简介
    快速排序

    20190827最新论文
    GNN
    Multimodal Machine Learning
    Wasserstein距离 及两多元高斯分布间的W距离
  • 原文地址:https://www.cnblogs.com/ajmddzp/p/10847476.html
Copyright © 2020-2023  润新知