给出一棵无根的树, 每个节点上有价值, 边上有花费, 每到一个点能得到这个点的价值, 每到一条边会减掉这个边的价值, 只有当前拿到的价值>花费才能走这条边, 在随意选择起点和终点的情况下, 问最多能得到多少价值.
思路: 这个题与那个Computer的树形dp很像, 无非是多了几个限定条件, 点有了价值, 边有了花费, 还是求最大价值.
Ⅰ首先将这个无根树固定一个根, 每个点就有了固定的父子关系. 这里是一个dfs.
Ⅱ 从叶子节点向根节点更新, 每个点保存最大值和次大值, 每个值都要记录其来源, 这是为了保证Ⅲ中能确定不会用自己更新自己.另外, 要注意每个点的最大值和次大值不仅包括儿子的贡献, 在初始时注意最大值应初始化为自己的点上权值.
Ⅲ 从根节点向叶子节点更新, 父亲的最大值/次大值只要不是由当前节点贡献, 那就要用来更新当前节点的最大值/次大值, 当然, 前提是这个值>=父亲→当前节点的距离.
这样, 每个点的最大值的最大值就是答案.
树形dp一步一步的写, 还是很好debug的.
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, int> pli;
typedef pair<int, int> pii;
const int M = 3e5 + 5;
const int inf = 1e9 + 5;
ll w[M];
vector<int> edge[M];//无根树的边
int n;
vector<int> sons[M];
int fa[M];
void dfs(int now) {
for (auto v:edge[now]) {
if (fa[now] == v)continue;//无根树是双向边, 当然不能让它跑父节点
fa[v] = now;
sons[now].push_back(v);//这样就变成单向边了
dfs(v);
}
}
ll dp[M][2];
int source[M][2];
map<pii, ll> dis;//两个点之间的边长
pli dfs2(int now) {// 返回 最大值和最大值来源
if (sons[now].empty()) {
dp[now][0] = w[now];
source[now][0] = now;
dp[now][1] = -1;
source[now][1] = -1;
return make_pair(w[now], -1);
}
pli m1 = make_pair(w[now], now), m2 = make_pair(-1, -1);
for (auto v:sons[now]) {
pli tmp = dfs2(v);
tmp.second = v;//这里返回的"来源"可能是"孙子", 要改成"儿子"
tmp.first -= dis[make_pair(now, v)];
if (tmp.first < 0)continue;
tmp.first += w[now];
if (tmp.first > m1.first) {
m2 = m1;
m1 = tmp;
} else if (tmp.first > m2.first) {
m2 = tmp;
}
}
if (m1.first < 0) {
dp[now][0] = -1;
source[now][0] = -1;
dp[now][1] = -1;
source[now][1] = -1;
return make_pair(-1, -1);
}
dp[now][0] = m1.first;
source[now][0] = m1.second;
dp[now][1] = m2.first;
source[now][1] = m2.second;
return m1;
}
void dfs3(int now) {
if (now != 1) {
for (int i = 0; i < 2; i++) {//用父节点的两个最大值更新
if (source[fa[now]][i] == now)continue;// 如果这个值来自自己, 当然不能用.
if (dp[fa[now]][i] > dis[make_pair(now, fa[now])]
&& dp[now][0] < dp[fa[now]][i] - dis[make_pair(now, fa[now])] + w[now]) {
source[now][1] = source[now][0];
dp[now][1] = dp[now][0];
source[now][0] = source[fa[now]][i];
dp[now][0] = dp[fa[now]][i] - dis[make_pair(now, fa[now])] + w[now];
} else if (dp[fa[now]][i] > dis[make_pair(now, fa[now])]
&& dp[now][1] < dp[fa[now]][i] - dis[make_pair(now, fa[now])] + w[now]) {
source[now][1] = source[fa[now]][i];
dp[now][1] = dp[fa[now]][i] - dis[make_pair(now, fa[now])] + w[now];
}
}
}
for (auto v:sons[now]) {
dfs3(v);
}
}
void solve() {
memset(fa, -1, sizeof(fa));
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%I64d", &w[i]);
}
for (int i = 0; i < n - 1; i++) {
int u, v;
ll diss;
scanf("%d%d%I64d", &u, &v, &diss);
dis[make_pair(u, v)] = dis[make_pair(v, u)] = diss;
edge[u].push_back(v);
edge[v].push_back(u);
}
int bigBoss = 1;//规定一个根
dfs(1);//转化为有根树
dfs2(1);//从叶到根更新dp和source
// for (int i = 1; i <= n; i++) {
// printf("[%d]dp1:%I64d, s1:%d, dp:%I64d, s2:%d \n", i, dp[i][0], source[i][0], dp[i][1], source[i][1]);
// }
dfs3(1);//从根到叶更新dp和source
ll ans = -inf;
for (int i = 1; i <= n; i++) {
ans = max(ans, dp[i][0]);
}
printf("%I64d\n", ans);
}
int main() {
solve();
return 0;
}