Codeforces Round #526 (Div. 2) D. The Fair Nut and the Best Path

D. The Fair Nut and the Best Path

题目链接https://codeforces.com/contest/1084/problem/D

题意:

给出一棵树,走不重复的路径,每到一个结点加上其权值,经过一条边减去其权值,路径中途减去后不能出现负数,问怎么选择路径可以让最后得到的最大。

题解:

这题考虑用dp来做。

我们定义dp[u]为走到u点的最大值,注意这里的方向,是走到u点。题目中的意思是路径不能走回头路。

对于一个父节点u,那么我们可以根据走到其儿子结点的最大值来更新经过父节点的路径的最大值。

如果两个儿子到u(dp[v1]+dp[v2]-dis(v1,u)-dis(v2,u))都为正数,那么此时ans = a[u]+dp[v1]+dp[v2]-dis(v1,u)-dis(v2,u)。在这里虽然dp指的是走到当前结点的最大值,但是反过来走并不影响其结果

现在说说反过来走为什么并不影响结果,假设对于从v1已经走到了u,现在u点的值为x。

设v2的dp值为y,dis(v2,u)=z 。假设y+z>0,但x+z<0,也就是不能从u走到v2时但可以从v2走到u时,我们求ans时,会更新x=x+y+z(实际并不能走过去),但根据x+z<0有x+y+z<y。

所以现在并不影响最大值。最后更新dp[u]时也不影响。

对于另外一种情况v2可以走到u,u也可以走到v2,这种情况的证明就显而易见了。

然后具体做法就是每次根据其儿子维护经过当前结点的最大值,在最后根据其儿子选择一个较大值更新当前结点的dp值。

这题要用大最大连续子段和的技巧...

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5+5;
int n;
ll a[N],dp[N];
ll ans;
vector <pair<int,int> > g[N];
void dfs(int node,int pa){
    ans=max(ans,a[node]);
    ll mx=0;
    for(auto v:g[node]){
        if(v.first==pa) continue ;
        dfs(v.first,node);
        ans=max(ans,mx+dp[v.first]-v.second+a[node]);
        mx=max(mx,dp[v.first]-v.second);
    }
    dp[node]=mx+a[node];
    return ;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%I64d",&a[i]);
    }
    for(int i=1;i<n;i++){
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c);
        g[u].push_back(make_pair(v,c));
        g[v].push_back(make_pair(u,c));
    }
    dfs(1,-1);
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/heyuhhh/p/10116517.html