P3387 模板缩点 [强连通分量][DAG]

Posted foxc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3387 模板缩点 [强连通分量][DAG]相关的知识,希望对你有一定的参考价值。

题意

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

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

Solution

先缩点,就成了一个DAG图,做一遍拓扑排序,按拓扑序进行DP。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 10010,M = 100010 * 2;
int head[N],ver[M],nxt[M],hc[N],vc[M],nc[M],tot,tc;
int ins[N],low[N],s[N],dfn[N],c[N],cnt,num,top,sum[N];
vector<int> scc[N];

void add(int x,int y){
    nxt[++tot] = head[x];
    ver[tot] = y;
    head[x] = tot;
}
void addc(int x,int y){
    nc[++tc] = hc[x];
    vc[tc] = y;
    hc[x] = tc;
}
void tarjan(int x){
    low[x] = dfn[x] = ++num;
    s[++top] = x; ins[x] = 1;
    for(int i = head[x]; i; i = nxt[i]){
        int v = ver[i];
        if(!dfn[v]){
            tarjan(v);
            low[x] = min(low[x],low[v]);
        }
        else if(ins[v])
            low[x] = min(low[x],dfn[v]);
    }
    if(low[x] == dfn[x]){
        cnt++; int y;
        do{
            y = s[top--]; ins[y] = 0;
            scc[cnt].push_back(y);
            c[y] = cnt;
        }while(x != y);
    }
}
int n,m,a[N],deg[N],dp[N];
void dfs(int x){
    dp[x] = sum[x];
    for(int i = hc[x]; i; i = nc[i]){
        int v = vc[i];
        dfs(v);
        dp[x] = max(dp[x],dp[v] + sum[x]);
    }
} 
int topo(){
    queue<int> q;
    for(int i = 1; i <= cnt; i++)
        if(!deg[i]) {
            q.push(i);
            dp[i] = sum[i];
        }
    while(!q.empty()){
        int x = q.front(); q.pop();
        for(int i = hc[x]; i; i = nc[i]){
            int y = vc[i];
            dp[y] = max(dp[y],dp[x] + sum[y]);
            deg[y]--;
            if(!deg[y]) 
                q.push(y);
        }
    }
    int ans = 0;
    for(int i = 1; i <= cnt; i++)
        ans = max(ans,dp[i]);
    return ans;
}

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    for(int i = 1; i <= m; i++){
        int u,v;
        cin >> u >> v;
        add(u,v);
    }
    for(int i = 1; i <= n; i++)
        if(!dfn[i]) tarjan(i);
    for(int x = 1; x <= n; x++){
        for(int i = head[x]; i; i = nxt[i]){
            int v = ver[i];
            if(c[x] == c[v]) continue;
            addc(c[x],c[v]);
            deg[c[v]]++;
        }
    }
    for(int i = 1; i <= cnt; i++){
        for(int j = 0; j < scc[i].size(); j++)
            sum[i] += a[scc[i][j]];
    }
    cout << topo() << endl;
    return 0;
}

以上是关于P3387 模板缩点 [强连通分量][DAG]的主要内容,如果未能解决你的问题,请参考以下文章

缩点(tarjan求强连通分量)

洛谷P3387 缩点模板

P3387 模板缩点 题解 (Tarjan)

P3387 模板缩点 && P3388 模板割点(割顶)

BZOJ 1179 Atm(强连通分量缩点+DP)

UVALIVE 4287 Proving Equivalences (强连通分量+缩点)