題目連結:HDU 2196
a,連線長度 L對於樹上任一節點,其最遠節點必定是樹直徑的兩個端點之一。但本題採用經典兩次 DFS 遞推(換根 DP)直接求出每節點答案:
dp[u][0]、dp[u][1]dfs(u, parent)void dfs(int u, int parent) {
for (int e = head[u]; e; e = edges[e].next) {
int v = edges[e].to, w = edges[e].w;
if (v == parent) continue;
dfs(v, u);
// 以 v 為子根的最長路徑 + 邊權
int cand = dp[v][0] + w;
if (cand >= dp[u][0]) {
dp[u][1] = dp[u][0]; // 原最長降為次長
dp[u][0] = cand; // 更新最長
} else if (cand > dp[u][1]) {
dp[u][1] = cand; // 更新次長
}
}
}
dp[u][0]:u 子樹內最長下行距離dp[u][1]:u 子樹內次長下行距離(需與最長來自不同子分支)solve(u, parent)void solve(int u, int parent) {
for (int e = head[u]; e; e = edges[e].next) {
int v = edges[e].to, w = edges[e].w;
if (v == parent) continue;
// 判斷 v 是否在 u 的「最長鏈」上
if (dp[u][1] == -1 || dp[u][0] - w != dp[v][0]) {
// v 不在最長鏈:u 的最長路徑可直接給 v 使用
dp[v][0] = dp[u][0] + w;
dp[v][1] = -1;
} else {
// v 在最長鏈上:v 不能用 u 的最長,改用次長
if (dp[u][1] + w >= dp[v][0]) {
dp[v][0] = dp[u][1] + w;
dp[v][1] = -1;
} else {
dp[v][1] = dp[u][1] + w; // 保留 v 原最長,更新次長
}
}
solve(v, u);
}
}
dp[u][0] - w == dp[v][0] 表示 v 正是貢獻 u 最長路徑的那個孩子dp[v][0],因父節點傳來的路徑必定優於或等於原值#include <bits/stdc++.h>
using namespace std;
const int N = 10010;
const int INF = 0x3f3f3f3f;
int dp[N][2], ans[N];
int head[N], tot;
struct Edge { int to, w, next; } e[N << 1];
inline void add_edge(int u, int v, int w) {
e[++tot] = {v, w, head[u]};
head[u] = tot;
}
void dfs(int u, int pre) {
dp[u][0] = dp[u][1] = -1;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to, w = e[i].w;
if (v == pre) continue;
dfs(v, u);
int cand = dp[v][0] + w;
if (cand >= dp[u][0]) {
dp[u][1] = dp[u][0];
dp[u][0] = cand;
} else if (cand > dp[u][1]) {
dp[u][1] = cand;
}
}
if (dp[u][0] == -1) dp[u][0] = 0; // 葉子節點
}
void solve(int u, int pre) {
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to, w = e[i].w;
if (v == pre) continue;
if (dp[u][1] == -1 || dp[u][0] - w != dp[v][0]) {
dp[v][0] = dp[u][0] + w;
dp[v][1] = -1;
} else {
if (dp[u][1] + w >= dp[v][0]) {
dp[v][0] = dp[u][1] + w;
dp[v][1] = -1;
} else {
dp[v][1] = dp[u][1] + w;
}
}
solve(v, u);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
while (cin >> n) {
memset(head, 0, sizeof(head));
memset(dp, 0, sizeof(dp));
tot = 0;
for (int i = 2; i <= n; i++) {
int a, L;
cin >> a >> L;
add_edge(i, a, L);
add_edge(a, i, L);
}
dfs(1, -1);
solve(1, -1);
for (int i = 1; i <= n; i++)
cout << dp[i][0] << '\n';
}
return 0;
}
dp[u][0]=0、dp[u][1]=-1while(cin>>n) 處理至 EOF此題是「樹上任意節點最遠距離」的教科書級範例,掌握兩次 DFS 遞推技巧,可舉一反三解決 POJ 1985、Codeforces 274B 等同類問題。