• Tarjan&&缩点简析


    由于昨天写计蒜客初赛的一道题,看出了是缩点,但一时忘记了另外一个叫什么s...的算法怎么写了,话说我为什么没有回去翻一下自己的blog然后今天就去学了更实用也更强力的Tarjan

    Tarjan的思想其实很简单,就是用时间戳(讲得真TM流弊,其实就是DFS访问到的次序)和栈来搞一下

    关于Tarjan的写法,我自己也不是很讲得来,大家可以看一下这篇blog

    这里讲一下如何用Tarjan来进行缩点

    令我们求出的col[i]表示原图中第i个点是哪个强连通分量中的,则我们可以建出新图,方式如下

    for (i=1;i<=n;++i)
    for (j=head[i];j!=-1;j=e[j].next)
    if (col[i]!=col[e[j].to]) add(col[i],col[e[j].to]);
    

    很玄学?但是它就是正确的,然后我们就得到了缩过点的新图

    像有这里有一道模板题:Luogu P3387 【模板】缩点

    题意很简单,我们先Tarjan缩点,缩完的点的点权就是它这个强连通分量中的所有点的点权和

    然后就是一个DAG(有向无环图)了,我们DP,记搜,拓扑排序都可以

    这里我还是习惯性的写了BFS的topo

    CODE

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=1e4+5,M=1e5+5;
    struct edge
    {
        int to,next;
    }e[M],ne[M];
    int n,m,head[N],nhead[N],a[N],v[N],dfn[N],low[N],stack[N],col[N],dis[N],q[N],ru[N],top,cnt,x,y,tot,sum;
    bool vis[N];
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch=tc();
        while (ch<'0'||ch>'9') ch=tc();
        while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    inline void add(int x,int y)
    {
        e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
    }
    inline void nadd(int x,int y)
    {
        ne[++cnt].to=y; ne[cnt].next=nhead[x]; nhead[x]=cnt;
    }
    inline int min(int a,int b)
    {
        return a<b?a:b;
    }
    inline int max(int a,int b)
    {
        return a>b?a:b;
    }
    inline void Tarjan(int now)
    {
        dfn[now]=low[now]=++tot;
        stack[++top]=now; vis[now]=1;
        for (register int i=head[now];i!=-1;i=e[i].next)
        if (!dfn[e[i].to])
        {
            Tarjan(e[i].to);
            low[now]=min(low[now],low[e[i].to]);
        } else
        {
            if (vis[e[i].to]) low[now]=min(low[now],dfn[e[i].to]);
        }
        if (dfn[now]==low[now])
        {
            col[now]=++sum; 
            v[sum]+=a[now]; vis[now]=0;
            while (now!=stack[top])
            {
                col[stack[top]]=sum;
                v[sum]+=a[stack[top]]; vis[stack[top--]]=0;
            } --top;
        }
    }
    inline int topo(void)
    {
        memset(dis,0,sizeof(dis));
        register int i; int ans=0;
        int H=0,T=0;
        for (i=1;i<=sum;++i)
        if (!ru[i]) q[++T]=i,dis[i]=v[i],ans=max(dis[i],ans);
        while (H<T)
        {
            int now=q[++H];
            for (i=nhead[now];i!=-1;i=ne[i].next)
            {
                dis[ne[i].to]=max(dis[ne[i].to],dis[now]+v[ne[i].to]);
                ans=max(ans,dis[ne[i].to]);
                if (!(--ru[ne[i].to])) q[++T]=ne[i].to;
            }
        }
        return ans;
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i,j;
        memset(head,-1,sizeof(head));
        memset(e,-1,sizeof(e));
        read(n); read(m);
        for (i=1;i<=n;++i)
        read(a[i]);
        for (i=1;i<=m;++i)
        read(x),read(y),add(x,y);
        for (i=1;i<=n;++i)
        if (!dfn[i]) Tarjan(i);
        memset(nhead,-1,sizeof(head));
        memset(ne,-1,sizeof(e)); cnt=0;
        for (i=1;i<=n;++i)
        for (j=head[i];j!=-1;j=e[j].next)
        if (col[i]!=col[e[j].to]) nadd(col[i],col[e[j].to]),++ru[col[e[j].to]];
        printf("%d",topo());
        return 0;
    }
    
  • 相关阅读:
    HDU 1859
    基本排序算法学习
    SQL 从指定表筛选指定行信息 获取表行数
    华为 2015 笔试题练习
    HDU 1003 maxsum
    $.ajax()方法详解
    【jQuery】jQ处理xml文件和xml字符串
    虚方法与抽象方法有什么区别
    System.IO.Path类
    FileAttributes枚举
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9032472.html
Copyright © 2020-2023  润新知