【模板】缩点

题目背景

缩点+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 <cstdio>
#include <queue>
#include <stack>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int maxn = 10010, maxm = 100010;

int n, m, head[maxn],sum[maxn],du[maxn], nextt[maxm], to[maxm], tot = 1, a[maxn], scc[maxn], top, pre[maxn], low[maxn], dfs_clock;
int Head[maxn], To[maxn], Nextt[maxn], Tot = 1, d[maxn], vis[maxn], ans;

void Add(int x, int y)
{
    To[Tot] = y;
    Nextt[Tot] = Head[x];
    Head[x] = Tot++;
}

void add(int x, int y)
{
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;
}

stack <int> s;

void tarjan(int u)
{
    pre[u] = low[u] = ++dfs_clock;
    s.push(u);
    for (int i = head[u]; i; i = nextt[i])
    {
        int v = to[i];
        if (!pre[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else
            if (!scc[v])
                low[u] = min(low[u], pre[v]);
    }
    if (pre[u] == low[u])
    {
        top++;
        while (1)
        {
            int t = s.top();
            s.pop();
            scc[t] = top;
            sum[top] += a[t];
            if (t == u)
                break;
        }
    }
}

void spfa(int s)
{
    memset(d, 0, sizeof(d));
    memset(vis, 0, sizeof(vis));
    d[s] = sum[s];
    vis[s] = 1;
    queue <int> q;
    q.push(s);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = Head[u]; i; i = Nextt[i])
        {
            int v = To[i];
            if (d[v] < d[u] + sum[v])
            {
                d[v] = sum[v] + d[u];
                if (!vis[v])
                {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    for (int i = 1; i <= top; i++)
        ans = max(ans, d[i]);
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        if (u != v)
        add(u, v);
    }
    for (int i = 1; i <= n; i++)
        if (!scc[i])
            tarjan(i);
    for (int i = 1; i <= n; i++)
        for (int j = head[i]; j; j = nextt[j])
        {
            int v = to[j];
            if (scc[i] != scc[v])
            {
                Add(scc[i], scc[v]);
                du[scc[v]]++;
            }
        }
    for (int i = 1; i <= top; i++)
        if (!du[i])
            spfa(i);
    printf("%d
", ans);

    return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/7815936.html