题意:
一颗树每个点有权值表示此点买入/卖出一本书的价格。还有n-1条边,每条边有一个权值表示经过该边需要的路费。求在树上某个起点买入一本书,某个终点卖出一本书的最大收益为多少。起点和终点可以为同一点。
思路:
树形DP:定义dp[u]表示在u点买入一本书在以u为根的子树中卖出一本树的最大收益。
因为题目要求任意起点,所以我们还需要对树进行旋转。旋转过程就是将父节点除指定子节点外其余节点视为一颗子节点的子树接入子节点。
C++代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
const int maxm = 200010;
int n,p[maxn],tol,head[maxn];
struct edge
{
int to,cost,next;
}es[maxm];
void addedge( int u , int v , int w )
{
es[tol].to = v;
es[tol].cost = w;
es[tol].next = head[u];
head[u] = tol++;
}
int dp[maxn],ans;
void dfs1( int u , int f )
{
dp[u] = p[u];
for ( int i=head[u] ; i!=-1 ; i=es[i].next )
{
int v = es[i].to,w = es[i].cost;
if ( v!=f )
{
dfs1( v , u );
if ( dp[v]-w>dp[u] )
dp[u] = dp[v]-w;
}
}
}
void dfs2( int u , int f )
{
ans = max ( ans , dp[u]-p[u] );
for ( int i=head[u] ; i!=-1 ; i=es[i].next )
{
int v = es[i].to,w = es[i].cost;
if ( v!=f )
{
if ( dp[u]-w>dp[v] )
dp[v] = dp[u]-w;
dfs2( v , u );
}
}
}
int main()
{
int T; scanf ( "%d" , &T );
while ( T-- )
{
scanf ( "%d" , &n );
for ( int i=1 ; i<=n ; i++ )
scanf ( "%d" , &p[i] );
tol = 0;
memset ( head , -1 , sizeof(head) );
for ( int i=1 ; i< n ; i++ )
{
int u,v,w;
scanf ( "%d%d%d" , &u , &v , &w );
addedge ( u , v , w );
addedge ( v , u , w );
}
ans = 0;
dfs1( 1 , 0 );
dfs2( 1 , 0 );
printf ( "%d\n" , ans );
}
return 0;
}