• 【图论】Tarjan 割点(割顶)


    割点的概念

    在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点(cut vertex / articulation point)。

    例如,在下图中,0、3是割点,因为将0和3中任意一个去掉之后,图就不再连通。如果去掉0,则图被分成1、2和3、4两个连通分量;如果去掉3,则图被分成0、1、2和4两个连通分量。

    怎么求割点

    Tarjan算法

    可以使用Tarjan算法求割点(注意,还有一个求连通分量的算法也叫Tarjan算法,与此算法类似)。(Tarjan,全名Robert Tarjan,美国计算机科学家。)

    首先选定一个根节点,从该根节点开始遍历整个图(使用DFS)。

    对于根节点,判断是不是割点很简单——计算其子树数量,如果有2棵即以上的子树,就是割点。因为如果去掉这个点,这两棵子树就不能互相到达。

    对于非根节点,判断是不是割点就有些麻烦了。我们维护两个数组dfn[]和low[],dfn[u]表示顶点u第几个被(首次)访问,low[u]表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。

    但这里也出现一个问题:怎么计算low[u]。

    假设当前顶点为u,则默认low[u]=dfn[u],即最早只能回溯到自身。

    有一条边(u, v),如果v未访问过,继续DFS,DFS完之后,low[u]=min(low[u], low[v]);

    如果v访问过(且u不是v的父亲),就不需要继续DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])。

    //以上摘自割点(Tarjan算法)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    struct edge{
        int nxt,mark;
    }pre[200010];
    int n,m,idx,cnt,tot;
    int head[100010],DFN[100010],LOW[100010];
    bool cut[100010];
    void add (int x,int y)
    {
        pre[++cnt].nxt=y;
        pre[cnt].mark=head[x];
        head[x]=cnt;
    }
    void tarjan (int u,int fa)
    {
        DFN[u]=LOW[u]=++idx;
        int child=0;
        for (int i=head[u];i!=0;i=pre[i].mark)
        {
            int nx=pre[i].nxt;
            if (!DFN[nx])
            {
                tarjan (nx,fa);
                LOW[u]=min (LOW[u],LOW[nx]);
                if (LOW[nx]>=DFN[u]&&u!=fa)
                    cut[u]=1;
                if(u==fa)
                    child++;
            }
            LOW[u]=min (LOW[u],DFN[nx]);
        }
        if (child>=2&&u==fa)
            cut[u]=1;
    }
    int main()
    {
        memset (DFN,0,sizeof (DFN));
        memset (head,0,sizeof (head));
        scanf ("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            int a,b;
            scanf ("%d%d",&a,&b);
            add (a,b);
            add (b,a);
        }
        for (int i=1;i<=n;i++)
            if (DFN[i]==0)
                tarjan (i,i);
        for (int i=1;i<=n;i++)
            if (cut[i])
                tot++;
        printf ("%d
    ",tot);
        for (int i=1;i<=n;i++)
            if (cut[i])
                printf ("%d ",i);
        return 0;
    }
    
  • 相关阅读:
    .NET 3.5新特性(转)
    (转)常用正则表达式
    IEC 61850(转)
    好几年了,我又回来了。
    EPR和SAP的一些名词解释(转载)
    为blogs添加风采,添加奥运金牌榜及赛程
    VS2010崩溃重启解决方法.
    C#制作Windows service服务系列二:演示一个定期执行的windows服务及调试(windows service)(转载)
    C#中操作XML (修改完整版) (转)
    C#制作Windows service服务系列一:制作一个可安装、可启动、可停止、可卸载的Windows service
  • 原文地址:https://www.cnblogs.com/PaulShi/p/10056847.html
Copyright © 2020-2023  润新知