• tarjan 缩点(模板)


    描述:

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

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

    思路:

      tarjan 的模板之一——缩点。先利用 tarjan 出图中的强连通分量及大小(点的权值),然后遍历所有点,重新构图(←重点),根据 topo DP一下,就可得出图中最大的权值和。

    标程:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<string>
    #include<cstdlib>
    #include<stack>
    #include<vector>
    #include<queue>
    #include<deque>
    #include<map>
    #include<set>
    using namespace std;
    #define maxn 1000005
    int n,m,cnt1,num,top,cnt2;//cnt1 作原图的前向星,cnt2 作新图的 
    int ins[maxn],head[maxn],nu[maxn],dfn[maxn],low[maxn];//nu 用来去除重边(重构图用),ins 记录强连通分量的大小 
    int st[maxn],co[maxn];//栈只为了表示此时是否有父子关系,co判断该点是否在栈中 
    int h[maxn],in[maxn],dis[maxn];//h相当于新图前向星的head,in统计点的入度,dis记录权值和(in,dis都做topo排序用) 
    int ans=0;//统计答案 
    struct hh
    {
        int to,next,from;//from,to有可以分别记录边的起点和终点 
    }t1[maxn],t2[maxn];//t1原图,t2新图 
    inline  int read()
    {
        int kr=1,xs=0;
        char ls;
        ls=getchar();
        while(!isdigit(ls))
        {
            if(!(ls^45))
                kr=-1;
            ls=getchar();
        }
        while(isdigit(ls))
        {
            xs=(xs<<1)+(xs<<3)+(ls^48);
            ls=getchar();
        }
        return xs*kr;
    }
    inline void add(int x,int y)
    {
        t1[++cnt1].next=head[x];
        t1[cnt1].from=x;
        t1[cnt1].to=y;
        head[x]=cnt1;
    }//存原图 
    inline void tarjan(int x)
    {
        low[x]=dfn[x]=++num;
        st[++top]=x;co[x]=1;
        for (int i=head[x];i;i=t1[i].next)
        {
            int v=t1[i].to;
            if(!dfn[v]) 
            {
            tarjan(v);
            low[x]=min(low[x],low[v]);
            }
            else if(co[v])
            {
                low[x]=min(low[x],low[v]);
            }
        }
        if (dfn[x]==low[x])
        {
            int y;
            while(y=st[top])
            {
                nu[y]=x;//表示:可以从x直接到达y(单向) 
                co[y]=0;//y出栈(记录清除) 
                if(x==y) break;
                ins[x]+=ins[y];//合并两个强连通分量
                --top;
            }
            --top;
        }
    }//日常操作 
    inline void topo()
    {
        queue <int> q;
        int tot=0;
        for (int i=1;i<=n;i++)
            if (nu[i]==i&&!in[i])//该点自己到达自己,且入度为0,表示为一个被缩为一点的强连通分量(且为起始点) 
            {
                q.push(i);
                dis[i]=ins[i];
            } 
        while (!q.empty())//依次取出所有的起点(入度为0的点) 
        {
            int k=q.front();q.pop();
            for (int i=h[k];i;i=t2[i].next)//遍历可以到达的点 
            {
                int v=t2[i].to;
                dis[v]=max(dis[v],dis[k]+ins[v]);//更新答案 
                in[v]--;//入度-- 
                if(in[v]==0)    q.push(v);//减到这个点入度为0,扔进队列,下次再取出作为起点 
            }
        }
        for (int i=1;i<=n;i++)
            ans=max(ans,dis[i]);//更新最终答案 
    }
    int main()
    {
        n=read();m=read();
        for (int i=1;i<=n;i++)
            ins[i]=read();//初始每个点认为是一个强连通分量(假装是) 
        for (int i=1;i<=m;i++)
        {
            int u,v;
            u=read();v=read();
            add(u,v);
        }
        for (int i=1;i<=n;i++)
            if(!dfn[i])
                tarjan(i);
        for (int i=1;i<=m;i++)
        {
            int x=nu[t1[i].from],y=nu[t1[i].to];
            if (x!=y)//←可以去除重边 
            {
                t2[++cnt2].next=h[x];
                t2[cnt2].to=y;
                t2[cnt2].from=x;
                h[x]=cnt2;//重构新图 
                in[y]++;
            }
        }
        topo();//topo 排序 
        printf("%d",ans);//输出 
    return 0;
    }
  • 相关阅读:
    Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树 矩阵面积并
    HDU 5787 K-wolf Number 数位DP
    python画图
    python文件操作
    python matplotlib绘图
    沟通
    ipython notebook的使用
    生活
    担心承担责任
    Large-scale Scene Understanding (LSUN)
  • 原文地址:https://www.cnblogs.com/lck-lck/p/9713113.html
Copyright © 2020-2023  润新知