题意
有一颗树,结点之间存在一定长度的边,求树上每一点到树上其他点的最远距离。
题目分析
刚写这个题目的时候我想着用了一下dfs,这样就可以得到每一个结点借助其子结点所能到达的最远距离,但是如果这个结点的最远距离需要经过其父节点才可以到达的话,这样就行不通了,所以还需要求出每个结点借助其父节点所能达到的最远距离(不可以经过当前这个结点),我们需要求的就是这二者中的最大值。
而我们在求每个结点借助其父节点所能达到的最大距离的时候有一个问题,因为我们求的是每个结点借助其子结点所能到达的最大距离和借助其父节点所能达到的最大距离两个数据,而在求的某个结点 x 的借助其父节点所能达到的最大距离的时候,需要知道其父节点不借助当前结点x所能到达的最大距离,但是当x的父节点pre借助其子结点所能到达的最大距离是通过x的,那么就不行了,因此,我们还需要求出 每个结点借助其子结点所能到达的最二大距离,这样就可以求出每个结点借助其父节点所能达到的最大距离了。
这样一来我们需要为每个结点维护的数据就有三个,记为:
dp [ v ] [ 0 ] , 代表 v 借助其子树获得的最大距离。
dp [ v ] [ 1 ], 代表v借助其子树获得的第二大距离
dp [ v ] [ 2 ], 代表v借助其父节点获得的最大距离
这样一来,我们对应每个结点到其他结点的最大距离就是 max( dp [ v ] [ 0 ], dp [ v ] [ 2 ]) 了
那么我们如何维护这三个数据呢?
1)dp [ v ] [ 0 ] , 这个很简单,dfs就可以得出,dp [ v ] [ 0 ] = max ( dp [ v ] [ 0 ], dp [ son ] [ 0 ] + w) 【son为 v 的子结点】
2)dp [ v ] [ 1 ] ,和求dp [ v ] [ 0 ] 相似,不过不可以从让dp [ v ] [ 0 ] 取最大的那个子结点得出dp [ v ] [ 1 ] , dp [ v ] [ 0 ] = max ( dp [ v ] [ 0 ], dp [ son ] [ 0 ] + w) 【son为 v 的子结点,但不是让dp [ v ] [ 0 ] 取最大的结点】
3)dp [ v ] [ 2 ] , 这个的话需要用其父节点的 dp [ v ] [ 0 ] , dp [ v ] [ 1 ] 和 dp [ v ] [ 2 ] 来确定,
如果当前结点 v 是其父节点 dp [ pre] [ 0 ] 取最大的时候的子结点,就不可以将pre结点的借助其子树获得的最大距离当作dp [ v ] [ 2 ],而可以将第二大的当作dp [ v ] [ 2 ] ,因此 dp [ v ] [ 2 ] = max ( dp [ pre] [ 1 ] , dp [ pre] [ 2 ] ),
如果当前结点 v 是不其父节点 dp [ pre] [ 0 ] 取最大的时候的子结点,就可以将pre结点的借助其子树获得的最大距离当作dp [ v ] [ 2 ],因此 dp [ v ] [ 2 ] = max ( dp [ pre] [ 0 ] , dp [ pre] [ 2 ] )
代码区
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int Max = 1e4+10;
struct Edge
{
int val;
int to;
int next;
}edge[Max<<1];
int head[Max], tot; //建图和存图用的
int dp[Max][3], path[Max];
/*
* path[v]代表v借助其子结点所能到达的最大距离,所经过的v的子结点
* dp[v][0]代表v借助其子树获得的最大距离
* dp[v][1]代表v借助其子树获得的第二大距离
* dp[v][2]代表v借助其父节点获得的最大距离
*/
void add(int u, int v, int val)
{
edge[tot].to = v;
edge[tot].val = val;
edge[tot].next = head[u];
head[u] = tot++;
}
void dfs1(int u, int pre)
{
dp[u][0] = 0;
dp[u][1] = 0;
dp[u][2] = 0; //初始化
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
int w = edge[i].val;
if (v == pre) continue;
dfs1(v, u);
if (dp[u][0] < dp[v][0] + w)
{
dp[u][0] = dp[v][0] + w;
path[u] = v; //记录当dp[u][0]取最大值的时候所借助的结点
}
}
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
int w = edge[i].val;
if (v == path[u] || v == pre) continue;//不可以是父节点或者当dp[u][0]取最大值的时候所借助的结点
dp[u][1] = max(dp[u][1], dp[v][0] + w);
}
}
void dfs2(int u, int pre)
{
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to, w = edge[i].val;
if (v == pre) continue;
if (path[u] == v) //父节点的最大距离是经过当前结点,那么dp[v][2]就只能从dp[u][1],dp[u][2]转移而来
{
dp[v][2] = max(dp[u][1], dp[u][2]) + w;
}
else //父节点的最大距离不经过当前结点,那么dp[v][2]就只能从dp[u][0],dp[u][2]转移而来
{
dp[v][2] = max(dp[u][0], dp[u][2]) + w;
}
dfs2(v, u);
}
}
int main()
{
std::ios::sync_with_stdio(false);
int n;
while (cin >> n)
{
tot = 0;
memset(head, -1, sizeof(head));
for (int i = 2;i <= n;i++)
{
int u, val;
cin >> u >> val;
add(u, i, val);
add(i, u, val);
}
dfs1(1, 0);
dfs2(1, 0);
for (int i = 1;i <= n;i++)
{
cout << max(dp[i][0], dp[i][2]) << endl;
//我们需要求的就是 结点借助其子结点所能到达的最远距离 和 每个结点借助其父节点所能达到的最远距离 中的最大值
}
}
return 0;
};