洛谷

洛谷题单指南-树与图上的动态规划-P2656 采蘑菇

原题链接:https://www.luogu.com.cn/problem/P2656

题意解读:有向图中,有边权,重复走获得边权值乘上系数,系数只有一位小数,问从起点能走过的路径的最大边权和。

解题思路:

既然可以重复走,可以先求强联通分量,对强联通分量进行缩点,并计算缩点内所有路径反复走能得到的最大边权和。

由于缩点之后,就具备了拓扑序,直接通过拓扑序进行DP,即可求得从起点到每个点的最长路径。

最后更新答案即可。

要点:

1、提前将一条边反复走得到的权值和预处理出来,由于系数只有一位小数,可以乘10,再除10,即可取整

2、缩点的时候,如果两个点在同一个scc则更新缩点里的边权和,如果不在同一个scc则连边

3、可以对缩点图进行拓扑排序求最长路,直接基于缩点图编号是拓扑序逆序的性质进行DP更简单

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 80005, M = 400005;

int head1[N], head2[N], w[M], maxw[M], to[M], nxt[M], idx;
int dfn[N], low[N], timestamp;
stack<int> stk;
bool vis[N];
int scc[N], sz[N], cnt; //sz[i]表示i所在scc内的最大边权和,考虑反复走
int f[N];
int n, m, s, ans;

void add1(int a, int b, int c, int d)
{
    to[++idx] = b;
    w[idx] = c;
    while(c) maxw[idx] += c, c = c * d / 10; //计算一条边反复使用后的最大边权
    nxt[idx] = head1[a];
    head1[a] = idx;
}

void add2(int a, int b, int c)
{
    to[++idx] = b;
    w[idx] = c;
    nxt[idx] = head2[a];
    head2[a] = idx;
}

void tarjan(int u)
{
    dfn[u] = low[u] = ++timestamp;
    stk.push(u);
    vis[u] = true;
    for(int i = head1[u]; i; i = nxt[i])
    {
        int v = to[i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(vis[v]) low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        cnt++;
        while(true)
        {
            int t = stk.top(); stk.pop();
            vis[t] = false;
            scc[t] = cnt;
            if(t == u) break;
        }
    }
}

int main()
{
    cin >> n >> m;
    while (m--)
    {
        int a, b, c;
        double d;
        cin >> a >> b >> c >> d;
        add1(a, b, c, d * 10);
    }
    cin >> s;
    tarjan(s);

    for(int u = 1; u <= n; u++)
    {
        for(int i = head1[u]; i; i = nxt[i])
        {
            int v = to[i];
            int a = scc[u], b = scc[v];
            if(a && b && a != b)
            {
                add2(a, b, w[i]);
            }
            if(a && b && a == b) //边在同一个scc内,累加sz
            {
                sz[a] += maxw[i];
            }
        }
    }

    for(int u = cnt; u >= 1; u--)
    {
        if(!f[u]) f[u] = sz[u]; //如果没有被更新过,初始化为sz
        for(int i = head2[u]; i; i = nxt[i])
        {
            int v = to[i];
            if(f[u] + w[i] + sz[v] > f[v])
            {
                f[v] = f[u] + w[i] + sz[v];
            }
        }
    }

    for(int i = 1; i <= cnt; i++) ans = max(ans, f[i]);
    cout << ans;

    return 0;
}