【模板】缩点 洛谷p3387

题目背景

缩点+DP

题目描述

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

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

输入输出格式

输入格式:

第一行,n,m

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

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

输出格式:

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

输入输出样例

输入样例#1:
2 2
1 1
1 2
2 1
输出样例#1:
2

说明

n<=10^4,m<=10^5,|点权|<=1000算法:Tarjan缩点+DAGdp


#include
#include
#include
#include
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const int MAXN=10005,MAXM=100005;
struct Edge{
    int v,next;
}e[MAXM<<1];
int n,m,head[MAXN<<1],tot,w[MAXN<<1],group[MAXN],ne;
int Dfn[MAXN],Low[MAXN],TIMES,inq[MAXN];
stack q;
int ans,vis[MAXN<<1],f[MAXN<<1];
inline void add(int u,int v)
{
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot++;
}
inline void Tarjan(int u)
{
    int i,node;
    Dfn[u]=Low[u]=++TIMES;
    q.push(u);
    inq[u]=1;
    for(i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        if(!Dfn[v]){
            Tarjan(v);
            Low[u]=min(Low[u],Low[v]);
        }
        else if(inq[v]) Low[u]=min(Low[u],Dfn[v]);
    }
    if(Dfn[u]==Low[u]){
        ne++;
        do{
            node=q.top();q.pop();
            inq[node]=0;
            group[node]=ne;
            w[ne]+=w[node];
        }while(node!=u);
    }
}
inline int dp(int u)
{
    int i;
    if(vis[u]) return f[u];
    for(i=head[u];~i;i=e[i].next){
        int v=e[i].v;
        f[u]=max(f[u],dp(v));
    }
    f[u]+=w[u];
    vis[u]=1;
    return f[u];
}
int main()
{
    ios::sync_with_stdio(false);
    memset(head,-1,sizeof(head));
    int i,j,u,v;
    cin>>n>>m;
    ne=n;
    f(i,1,n){
        cin>>w[i];
    }
    f(i,1,m){
        cin>>u>>v;
        add(u,v);
    }
    f(i,1,n){
        if(Dfn[i]) continue;
        Tarjan(i);
    }
    f(i,1,n){
        for(j=head[i];~j;j=e[j].next){
            int v=e[j].v;
            if(group[i]==group[v]) continue;
            add(group[i],group[v]);
        }
    }
    f(i,n+1,ne){
        ans=max(ans,dp(i));
    }
    cout<     return 0;
}

你可能感兴趣的:(动态规划)