zoukankan      html  css  js  c++  java
  • 缩点+割点(tarjan)

    1.tarjan缩点

    思路:找到图中的强联通分量,染成同色,形成DAG(有向无环图).

    vis:记录点是否在栈中.

    dfn:点在dfs中顺序,即时间戳.

    col:同一强连通分量的染色颜色.

    low:点与点的子辈能回到的最小时间戳.

    其实也可以有其他辅助数组,但核心的数组就这四个.

    难点:1.low[x]=min(low[x],low[to]),其实就是不断用子辈low更新,定义可知.

    2.dfn[x]=low[x]进行染色操作,用子辈更新low完后回溯发现dfn[x]=low[x],x为强连通分量的根.

    以lg3387缩点为例

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define R register
    #define e exit(0)
    const int maxn=1e5+10;
    int n,m,vd=-1,cnt,sum,top,deep,vis[maxn],val[maxn],head[maxn],dfn[maxn],ans[maxn];
    int cnt1,head1[maxn],low[maxn],stact[maxn],col[maxn],check[1010][1010],cd[maxn];
    struct bian{
        int to,next;
    }len[maxn];
    struct bian1{
        int to,next;
    }len1[maxn];
    inline int fd()
    {
        int s=1,t=0;
        char c=getchar();
        while(c<'0'||c>'9')
        {
            if(c=='-')
                s=-1;
            c=getchar();
        }
        while(c>='0'&&c<='9')
        {
            t=t*10+c-'0';
            c=getchar();
        }
        return s*t;
    }
    void add(int from,int to)
    {
        len[++cnt].to=to;
        len[cnt].next=head[from];
        head[from]=cnt;
    }
    void add1(int from,int to)
    {
        len1[++cnt1].to=to;
        len1[cnt1].next=head1[from];
        head1[from]=cnt1;
    }
    void tarjan(int x)
    {
        dfn[x]=++deep;
        low[x]=deep;
        stact[++top]=x;
        vis[x]=1;
        for(R int k=head[x];k;k=len[k].next)
        {
            int to=len[k].to;
            if(!dfn[to])
            {
                tarjan(to);
                low[x]=min(low[x],low[to]);
            }
            else if(vis[to])
                low[x]=min(low[x],low[to]);
        }
        if(dfn[x]==low[x])
        {
            int va=0;
            col[x]=++sum;
            vis[x]=0;
            while(stact[top]!=x)
            {
                va+=val[stact[top]];
                col[stact[top]]=sum;
                vis[stact[top--]]=0;
            }
            va+=val[stact[top]];
            ans[sum]=va;
            --top;
        }
    }
    void dfs(int x,int sumv)
    {
        vd=max(sumv,vd);
        for(R int k=head1[x];k;k=len1[k].next)
        {
            int to=len1[k].to;
            int nowsum=sumv+ans[to];
            dfs(to,nowsum);
        }
    }
    int main()
    {
        //freopen("s.in","r",stdin);
        //freopen("s.out","w",stdout);
        n=fd(),m=fd();
        memset(check,0,sizeof(check));
        for(R int i=1;i<=n;++i)
            val[i]=fd();
        for(R int i=1;i<=m;++i)
        {
            int from=fd(),to=fd();
            add(from,to);
        }
        for(R int i=1;i<=n;++i)
            if(!dfn[i])
                tarjan(i);
        if(sum==1)
        {
            printf("%d",ans[1]);
            return 0;
        }
        for(R int i=1;i<=n;++i)
            for(R int k=head[i];k;k=len[k].next)
            {
                int to=len[k].to;
                if(col[i]!=col[to])
                {
                    int num1=col[i],num2=col[to];
                    if(!check[num1][num2])
                    {
                        check[num1][num2]=1;
                        add1(num1,num2);
                        cd[num1]=1;
                    }
                }
            }
        for(R int i=1;i<=n;++i)
            if(cd[i])
                dfs(i,ans[i]);
        printf("%d",vd);
        return 0;
    }

    2.tarjan求割点.

    为割点:1.发现子结点并不能通过其他路径到达比祖先深度更浅的节点,祖先为割点.

    2.为为枚举的根节点,并且发现其有两个即以上的儿子,这个根节点为割点.

    low跟新:low[x]=min(low[x],low[to]),与上面一致.

    第二处:if(to!=fa&&dfn[to]<dfn[x]) low[x]=min(low[x],dfn[to]),在各大博客未找到证明.

    以lg3388为例:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define R register
    const int maxn=1e5+10;
    int n,m,cnt,ans,deep,lenth,head[maxn],dfn[maxn],low[maxn],cut[maxn],q[maxn];
    struct bian{
        int to,next;
    }len[maxn<<1];
    inline int fd()
    {
        int s=1,t=0;
        char c=getchar();
        while(c<'0'||c>'9')
        {
            if(c=='-')
                s=-1;
            c=getchar();
        }
        while(c>='0'&&c<='9')
        {
            t=t*10+c-'0';
            c=getchar();
        }
        return s*t;
    }
    void add(int from,int to)
    {
        len[++cnt].to=to;
        len[cnt].next=head[from];
        head[from]=cnt;
    }
    int tarjan(int x,int fa)
    {
        int child=0,lowx;
        lowx=dfn[x]=++deep;
        for(R int k=head[x];k;k=len[k].next)
        {
            int to=len[k].to;
            if(!dfn[to])
            {
                ++child;
                int lowto=tarjan(to,x);
                lowx=min(lowx,lowto);
                if(lowto>=dfn[x])
                    cut[x]=1;
            }
            else if(to!=fa&&dfn[x]>dfn[to])
                lowx=min(lowx,dfn[to]);//low[to]可能走到另一个环,到达更浅的祖先.
                //此时lowto<dfn[x]而忽略x这个可能的割点.
        }
        if(fa<0&&child==1)
            cut[x]=0;
        low[x]=lowx;
        return lowx;
    }
    int main()
    {
        //freopen("s.in","r",stdin);
        //freopen("s.out","w",stdout);
        n=fd(),m=fd();
        for(R int i=1;i<=m;++i)
        {
            int from=fd(),to=fd();
            add(from,to),add(to,from);
        }
        for(R int i=1;i<=n;++i)
            if(!dfn[i])
                tarjan(i,-1);
        for(R int i=1;i<=n;++i)
            if(cut[i])
            {
                ++ans;
                q[++lenth]=i;
            }
        printf("%d
    ",ans);
        sort(q+1,q+1+lenth);
        for(R int i=1;i<=lenth;++i)
            printf("%d ",q[i]);
        return 0;
    }

    附上dalao博客

  • 相关阅读:
    【LeetCode】70. 爬楼梯
    C++Socket编程—socket网络模型之IOCP
    leetcode_买卖股票_dp状态机
    leetcode_买卖股票_暴力递归
    tensorflow正则化
    程序员掌握这些面试技巧,成功上岸!
    阶段二Linux 高级编程:Linux基础命令三019
    阶段二Linux 高级编程:Linux基础命令二018
    阶段二Linux 高级编程:Linux基础命令一017
    阶段一Python核心编程:面向对象版学员管理系统016
  • 原文地址:https://www.cnblogs.com/xqysckt/p/11194867.html
Copyright © 2011-2022 走看看