【模板】缩点(tarjan,DAG上DP)

题目背景

缩点+DP

题目描述

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

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

输入输出格式

输入格式:

第一行,n,m

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

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

输出格式:

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

思路:

显然,由于点权为正,所以位于一个强连通分量中的结点,自然走得越多答案越大

所以,我们可以跑一边tarjan,将所有的强连通分量染色,建一个新的图

其中新图上的每个结点都代表旧图上的一个强连通分量

在跑tarjan染色的同时我们可以累加得到新图上每个点的点权

最后在新图上跑一边DP或者记忆化搜索即可

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define rii register int i
#define rij register int j
using namespace std;
struct yb{
    int from,to;
}y[100005];
struct ljb{
    int to,nxt;
}x[100005];
int tot,dq[10005],sta[10005],sum[10005],head[10005],bnt,last[10005];
int n,m,dfn[10005],low[10005],top,cnt,vis[10005],color[10005],dp[10005];
void add(int from,int to)
{
    bnt++;
    x[bnt].to=to;
    if(head[from]==0)
    {
        head[from]=bnt;
    }
    if(last[from]!=0)
    {
        x[last[from]].nxt=bnt;
    }
    last[from]=bnt;
}
void search(int wz)
{
    if(dp[wz]!=0)
    {
        return;
    }
    dp[wz]=sum[wz];
    int maxn=0;
    for(rii=head[wz];i!=0;i=x[i].nxt)
    {
        int ltt=x[i].to;
        if(dp[ltt]==0)
        {
            search(ltt);
        }
        maxn=max(maxn,dp[ltt]);
    }
    dp[wz]+=maxn;
}
void tarjan(int wz)
{
    cnt++;
    low[wz]=cnt;
    dfn[wz]=cnt;
    top++;
    sta[top]=wz;
    vis[wz]=1;
    for(rii=head[wz];i!=0;i=x[i].nxt)
    {
        int ltt=x[i].to;
        if(dfn[ltt]==0)
        {
            tarjan(ltt);
            low[wz]=min(low[wz],low[ltt]);
        }
        else
        {
            if(vis[ltt]==1)
            {
                low[wz]=min(low[wz],dfn[ltt]);
            }
        }
    }
    if(dfn[wz]==low[wz])
    {
        tot++;
        while(sta[top+1]!=wz)
        {
            color[sta[top]]=tot;
            sum[tot]+=dq[sta[top]];
            vis[sta[top]]=0;
            top--;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(rii=1;i<=n;i++)
    {
        scanf("%d",&dq[i]);
    }
    for(rii=1;i<=m;i++)
    {
        scanf("%d%d",&y[i].from,&y[i].to);
        add(y[i].from,y[i].to);
    }
    for(rii=1;i<=n;i++)
    {
        if(dfn[i]==0)
        {
            tarjan(i);
        }
    }
/*
    for(rii=1;i<=n;i++)
    {
        printf("%d ",color[i]);
    }
    cout<<endl;
    */
    bnt=0;
    memset(head,0,sizeof(head));
    memset(last,0,sizeof(last));
    memset(x,0,sizeof(x));
    for(rii=1;i<=m;i++)
    {
        if(color[y[i].from]!=color[y[i].to])
        {
            add(color[y[i].from],color[y[i].to]);
        }    
    }
    int ans=0;
    for(rii=1;i<=n;i++)
    {
        if(dp[i]==0)
        {
            search(i);
        }
        ans=max(ans,dp[i]);
    }
    printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/ztz11/p/9810430.html