Computer
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6492 Accepted Submission(s): 3273
![](https://i-blog.csdnimg.cn/blog_migrate/3f5ab5c3d97280e16503ce9ef614b5c5.jpeg)
Hint: the example input is corresponding to this graph. And from the graph, you can see that the computer 4 is farthest one from 1, so S1 = 3. Computer 4 and 5 are the farthest ones from 2, so S2 = 2. Computer 5 is the farthest one from 3, so S3 = 3. we also get S4 = 4, S5 = 4.
5 1 1 2 1 3 1 1 1
3 2 3 4 4
由于对于一个节点来说,可能得到的距离最大的值的路径来自他的子树,或者从他的父节点过来,所以用两次DFS。
第一次DFS求出所有节点在他的子树范围内到叶子节点距离的最大值和第二大的值,第二次DFS更新从父节点过来的情况就可以了。
因为如果只存最大值的话,判断一个点的从父节点过来的最大值,那么如果他的父节点存的最大值正好是从该点过来的,那么就失去了从父节点过来的状态,所以要记录最大的两个值。
如果对于求第二大值还是不理解的话:可以看一下这篇博客,但是我认为他其中有一点点交代不清,故做了一些小更改,如下。传送门
思路:
把无根树转化成有根树分析,
对于上面那棵树,要求距结点2的最长距离,那么,就需要知道以2为顶点的子树(蓝色圈起的部分,我们叫它Tree(2)),距顶点2的最远距离L1
还有知道2的父节点1为根节点的树Tree(1)部分(即红色圈起部分),距离结点1的最长距离+dist(1,2) = L2,那么最终距离结点2最远的距离就是max{L1,L2}
f[i][0],表示顶点为i的子树的,距顶点i的最长距离
f[i][1],表示Tree(i的父节点)的最长距离+i跟i的父节点距离
要求所有的f[i][0]很简单,只要先做一次dfs求每个结点到叶子结点的最长距离即可。
然后要求f[i][1], 可以从父节点递推到子节点,
假设节点u有n个子节点,分别是v1,v2...vn
那么
如果vi不是u最长距离经过的节点,f[vi][1] = dist(vi,u)+max(f[u][0], f[u][1])
如果vi是u最长距离经过的节点,那么不能选择f[u][0],因为这保存的就是最长距离,要选择Tree(u)第二大距离secondDist,
可得f[vi][1] = dist(vi, u) + max(secondDist, f[u][1])
收获:
1.代码实现中,dfs1与dfs2在搜索的方式上是有不同的。dfs1找的是顶点u的最长距离,每层的dfs1需要下一层的dfs1处理完毕返回后才能更新最长距离。而dfs2是一层处理完接着一层,从树的根节点开始,逐层向下,不需要下一层的返回就能完成更新。
2.dfs2中v才是当前需要更新最长距离的点,u是v的父节点,p是u的父节点。禁止v==p,也就是禁止找u的父节点,是为了防止dfs2返回到上层,导致递归没有了出口。在更新v的时候,u的最长距离就是最终的答案,所以使用u的次小距离实际上是不会有错的,这里需要仔细想想。
3.每次更新值都是先与次小距离比较,再是次小距离与最长距离比较,然后更新。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAXN = 10010;
struct Node
{
int to, next, w;
}edge[MAXN*2];//因为存无向边,所以需要2倍
int tol;
int head[MAXN];//头结点
int maxn[MAXN];//该节点往下到叶子的最大距离
int smaxn[MAXN];//次大距离
int maxid[MAXN];//最大距离对应的相邻节点序号
int smaxid[MAXN];//次大的序号
void init()
{
tol = 0;
memset(head, -1, sizeof(head));
}
void add(int u, int v, int w)
{
edge[tol].to = v;
edge[tol].w = w;
edge[tol].next = head[u];
head[u] = tol++;
edge[tol].to = u;
edge[tol].w = w;
edge[tol].next = head[v];
head[v] = tol++;
}
//求结点u往下到叶子结点的最大距离
//p是u的父亲结点
void dfs1(int u, int p)
{
maxn[u] = 0;//每次都必须赋值0,因为是多组数据,否则会导致错误
smaxn[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(v == p)//不能往上找父亲结点
continue;
dfs1(v, u);
if(smaxn[u] < edge[i].w + maxn[v])
{
smaxn[u] = edge[i].w + maxn[v];
smaxid[u] = v;
if(smaxn[u] > maxn[u])
{
swap(smaxn[u], maxn[u]);
swap(smaxid[u], maxid[u]);
}
}
}
}
//u为v的父节点,求出父节点u的最长距离+uv之间的距离来更新v的最长距离
void dfs2(int u, int p)
{
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;//v才是当前待更新最长距离的节点,不是u
if(v == p)//如果没有禁止往上找父节点,下面的dfs2会导致父子节点之间不断相互调用dfs2
continue;
if(v == maxid[u])//如果u的最长距离经过v
{
if(smaxn[u] + edge[i].w > smaxn[v])
{
smaxn[v] = smaxn[u] + edge[i].w;
smaxid[v] = u;
if(smaxn[v] > maxn[v])
{
swap(smaxn[v], maxn[v]);
swap(smaxid[v], maxid[v]);
}
}
}
else
{
if(maxn[u] + edge[i].w > smaxn[v])
{
smaxn[v] = maxn[u] + edge[i].w;
smaxid[v] = u;
if(smaxn[v] > maxn[v])
{
swap(smaxn[v], maxn[v]);
swap(smaxid[v], maxid[v]);
}
}
}
dfs2(v, u);
}
}
int main()
{
int n;
int to, w;
while(cin >> n)
{
init();
for(int i = 2; i <= n; i++)
{
cin >> to >> w;
add(i, to, w);
}
dfs1(1, -1);
dfs2(1, -1);
for(int i = 1; i <= n; i++)
printf("%d\n", maxn[i]);
}
return 0;
}