[模板]强连通分量 - 缩点

题面描述

一个有向图,求出一条路径,可以经过一个点多次,求路径最大点权和。

Solution

使用tarjan查找强连通分量,缩点,重新建图,就得到一个DAG,且每个点为各自的强连通分量的点权和,跑一遍dp即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 10005
#define MAXM 100006

int u[MAXM],v[MAXM];
struct edge{
    int v,next;
}G[MAXM];

int head[MAXN],vis[MAXN],dfn[MAXN],low[MAXN];
int color[MAXN],point[MAXN],size[MAXN];
int dp[MAXN];
int top = 0;int st[MAXN];
int num = 0;

int N,M;
int tot = 0;
inline void add(int u,int v){
    G[++tot].v = v;G[tot].next = head[u];head[u] = tot;
}

void tarjan(int u){
    
    color[u] = u;
    dfn[u] = ++num; low[u] = dfn[u]; 
    vis[u] = 1;
    st[++top] = u;

    for(register int i=head[u];i;i=G[i].next){
        int v = G[i].v;
        if(!dfn[v])tarjan(v);
        if(vis[v])low[u] = std::min(low[u],low[v]);
    }

    if(low[u]==dfn[u]){
        vis[u] = 0;
        while(st[top]!=u){
            color[st[top]] = u;
            vis[st[top]] = 0;
            top--;
        }
        top--;
    }
}

int ans = 0;
int dfs(int u,int rt){
    if(dp[u])return dp[u];
    int maxx = 0;
    for(register int i=head[u];i;i=G[i].next){
        int v = G[i].v;
        if(v==rt)continue;
        int cur = dfs(v,u);
        if(cur>maxx)maxx = cur;
    }
    return dp[u] = point[u]+maxx;
}

int score[MAXN];

int main(){

    std::memset(head,0,sizeof(head));
    std::memset(vis,0,sizeof(vis));
    std::memset(color,0,sizeof(color));
    std::memset(dfn,0,sizeof(dfn));

    scanf("%d%d",&N,&M);
    for(register int i=1;i<=N;++i)scanf("%d",&score[i]);
    for(register int i=1;i<=M;++i){
        scanf("%d%d",&u[i],&v[i]);
        add(u[i],v[i]);
    }

    for(register int i=1;i<=N;++i){
        if(!dfn[i])tarjan(i);
    }

    std::memset(head,0,sizeof(head));
    std::memset(G,0,sizeof(G));
    std::memset(size,0,sizeof(size));

    for(register int i=1;i<=M;++i){
        if(color[u[i]]==color[v[i]])continue;
        add(color[u[i]],color[v[i]]);
        size[color[v[i]]]++;
    }

    std::memset(point,0,sizeof(point));
    std::memset(dp,0,sizeof(dp));
    for(register int i=1;i<=N;++i){
        point[color[i]]+=score[i];
    }

    for(register int i=1;i<=N;++i){
        if(size[i]!=0)continue;
        int cur = dfs(i,0);
        if(cur>ans)ans = cur;
    }

    printf("%d",ans);
    return 0;
}
posted @ 2018-08-23 13:48  Neworld1111  阅读(175)  评论(0编辑  收藏  举报