1. 程式人生 > >Codeforces Round #526 (Div. 2) D. The Fair Nut and the Best Path 樹形dp

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

題目連結:

傳送門


題意:

每個點有正權值ai,每條邊有負權值wi,你可以隨意選擇一條路徑,使得這條路徑的總權值最大,要求每個點每條邊至多都只能走一次。


思路:
一個頂點可以是路徑中的根節點或者是中間節點。

所以,設立兩個陣列,dpr,dpm.

dpr表示為根節點的最大權值。

dpm表示為中間節點的最大權值。

(1)為根節點

        dpr[u]=w[u]+dpr[v]-edge<u,v>;

   (2)為中間節點

        求出其中與其相連的兩個節點,分別為最大值和次大值.

        dpm[u]=w[u]+dpr[v1]-edge<u,v1>+dpr[v2]-edge<u,v2>;

然後求出最大值即可。


程式碼如下:
 

#include <bits/stdc++.h>
using namespace std;
const int maxn=3*1e5+5;
typedef long long ll;
ll w[maxn];
int n;
int head[maxn];
ll dpr[maxn];
ll dpm[maxn];
ll ans=-1;
struct  edge
{
    int to;
    int next;
    ll len;
};
edge e[maxn*2];
void addedge(int v,int u,int id,ll len)
{
    e[id].to=v;
    e[id].len=len;
    e[id].next=head[u];
    head[u]=id;
}
void init()
{
    for (int i=1;i<=n;i++)
    {
        dpm[i]=dpr[i]=w[i];
    }
}
void dfs(int now,int pre)
{
    ll mx,mm;
    mx=mm=0;
    for (int i=head[now];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(pre==v)
            continue;
        dfs(v,now);
        if(dpr[v]-e[i].len>mx)
        {
            mm=mx;
            mx=dpr[v]-e[i].len;
        }
        else if(dpr[v]-e[i].len>mm)
        {
            mm=dpr[v]-e[i].len;
        }
    }
    dpm[now]+=mm+mx;
    dpr[now]+=mx;
    ans=max(dpm[now],ans);
    ans=max(dpr[now],ans);
}
int main()
{
    memset (head,-1,sizeof(head));
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",&w[i]);
    }
    for (int i=0,id=0;i<n-1;i++)
    {
        int x,y;
        ll len;
        scanf("%d%d%lld",&x,&y,&len);
        addedge(y,x,id++,len);
        addedge(x,y,id++,len);

    }
    init();
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
}