计算机(换根dp)⭐
题面
一所学校前一段时间买了第一台计算机(所以这台计算机的ID是1)。
近年来,学校又购买了N-1台新计算机。
每台新计算机都与之前买进的计算机中的一台建立连接。
现在请你求出第i台计算机到距离其最远的计算机的电缆长度。
例如,上图中距离计算机1最远的是计算机4,因此 S1=3;距离计算机2最远的是计算机4和5,因此 S2=2;距离计算机3最远的是计算机5,所以 S3=3;同理,我们也得到 S4=4,S5=4。
输入格式
输入包含多测试数据。
每组测试数据第一行包含整数N。
接下来N-1行,每行包含两个整数,第 i 行的第一个整数表示第 i 台电脑买入时连接的电脑编号,第二个整数表示这次连接花费的电缆长度。
输出格式
每组测试数据输出N行。
第 i 行输出第 i 台电脑的 Si。
数据范围
1≤N≤10000,
电缆总长度不超过109
输入样例:
5
1 1
2 1
3 1
1 1
输出样例:
3
2
3
4
4
题解
标准的换根dp
首先想到对于定根为1 f[u] = max(f[u], f[son] + cost)
然后递归dfs, 就可以解出根为1的ans
怎么换根呢? 也是dfs, f[son] = max(f[son], f[u] + cost)
问题来了!
你怎么知道f[u]不是通过f[son]得到的呢?
这样不就套娃了? 根 根据儿子得到最大值, 儿子又通过根得到最大值
所以自然想到 设状态为 f[u][2]
f[u][0] = <根为u的最大值, 是通过儿子x得到的>
f[u][1] = <根为u的次大值, 是通过儿子y得到的> (y != x, f[u][1] <= f[u][0])
换根的时候判断一下是选 f[u][0] 还是 f[u][1] 转移就好了
至于复杂度, 两个dfs深度相同, 每个dfs每个节点就访问为一次
代码
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define fi first
#define se second
using namespace std;
typedef pair<int, int> PII;
const int maxn = 10005;
int n, ans[maxn];
int h[maxn], to[maxn], ne[maxn], co[maxn], tot;
PII f[maxn][2];
void add(int u, int v, int c)
{
ne[++tot] = h[u]; h[u] = tot; co[tot] = c; to[tot] = v;
}
void dp(int u)
{
for (int i = h[u]; i; i = ne[i])
{
int& v = to[i];
dp(v);
if (f[v][0].fi + co[i] > f[u][0].fi)
f[u][1] = f[u][0], f[u][0] = { f[v][0].fi + co[i], v };
else if (f[v][0].fi + co[i] > f[u][1].fi)
f[u][1] = { f[v][0].fi + co[i], v };
}
}
void dfs(int u, int fa, int c)
{
int cost = c + (f[fa][0].se == u ? f[fa][1].fi : f[fa][0].fi);
if (cost > f[u][0].fi) f[u][0] = { cost, fa };
else if (cost > f[u][1].fi) f[u][1] = { cost, fa };
ans[u] = f[u][0].fi;
for (int i = h[u]; i; i = ne[i]) dfs(to[i], u, co[i]);
}
int main()
{
while (cin >> n)
{
memset(h, 0, sizeof h); tot = 0;
memset(f, 0, sizeof f);
for (int i = 2, u, c; i <= n; ++i)
{
cin >> u >> c;
add(u, i, c);
}
dp(1);
dfs(1, 0, 0);
rep (i, 1, n) cout << ans[i] << '\n';
}
return 0;
}