A - 氪金带东
题目描述
实验室里原先有一台电脑(编号为1),最近氪金带师咕咕东又为实验室购置了N-1台电脑,编号为2到N。每台电脑都用网线连接到一台先前安装的电脑上。但是咕咕东担心网速太慢,他希望知道第i台电脑到其他电脑的最大网线长度,但是可怜的咕咕东在不久前刚刚遭受了宇宙射线的降智打击,请你帮帮他。
提示: 样例输入对应这个图,从这个图中你可以看出,距离1号电脑最远的电脑是4号电脑,他们之间的距离是3。 4号电脑与5号电脑都是距离2号电脑最远的点,故其答案是2。5号电脑距离3号电脑最远,故对于3号电脑来说它的答案是3。同样的我们可以计算出4号电脑和5号电脑的答案是4.
Input
输入文件包含多组测试数据。对于每组测试数据,第一行一个整数N (N<=10000),接下来有N-1行,每一行两个数,对于第i行的两个数,它们表示与i号电脑连接的电脑编号以及它们之间网线的长度。网线的总长度不会超过10^9,每个数之间用一个空格隔开。
Output
对于每组测试数据输出N行,第i行表示i号电脑的答案 (1<=i<=N).
Sample Input
5
1 1
2 1
3 1
1 1
Sample Output
3
2
3
4
4
解题思路
一台电脑看成一个点,所有电脑看成一棵树,而每棵树有两个直径端点。本题意为求每个点至另一个点的最大距离,即每个点至两个端点距离中的较大距离。使用链式前向星方式储存树,并从任意起点开始进行dfs搜索距离最远的点,则此次dfs的搜索结果必为端点之一;再dfs此端点,搜索结果为另一端点,在沿途记录下至个点的代价,则通过此次dfs获得所有点至该端点的距离。第三次dfs另一端点,记录下至各点的代价,获得该端点至所有点的距离。每个点将至两端点的距离取最大值,即为题目答案。复杂度为O(n)。
代码
#include <iostream>
#include <cstring>
#include <cmath>
#define llint long long int
#define maxn 100100
using namespace std;
int head[maxn],tot,N,dia_v;
llint ans,dis[maxn],maxd,d2[maxn];
bool vis[maxn];
llint max(llint a,llint b) {
if(a>=b) return a;
else return b;
}
struct edge {
int u,v,nxt;
llint w;
}Edges[maxn];
void init(int n) {
tot=0,maxd=0,dia_v=0,ans=0;
for(int i=1;i<=n;i++) head[i]=-1;
memset(vis,0,sizeof vis);
memset(dis,0,sizeof dis);
}
void addEdge(int u,int v,llint w) {
Edges[tot].u=u;
Edges[tot].v=v;
Edges[tot].w=w;
Edges[tot].nxt=head[u];
head[u]=tot;
tot++;
}
void dfs(int u) {
vis[u]=true;
for(int i=head[u];i!=-1;i=Edges[i].nxt) {
if(vis[Edges[i].v]==false) {
vis[Edges[i].v]=true;
dis[Edges[i].v]=dis[u]+Edges[i].w;
if(dis[Edges[i].v]>=maxd) { //找端点
maxd=dis[Edges[i].v];
dia_v=Edges[i].v;
}
dfs(Edges[i].v);
}
}
return;
}
int main() {
while(~scanf("%d",&N)) {
init(N);
for(int i=2;i<=N;i++) {
int v_;
llint w_;
scanf("%d%d",&v_,&w_);
addEdge(i,v_,w_);
addEdge(v_,i,w_);
}
dfs(1);
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
dfs(dia_v);
for(int i=1;i<=N;i++) d2[i] = dis[i];
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
dfs(dia_v);
for(int i=1;i<=N;i++) printf("%lld\n",max(dis[i],d2[i]));
}
return 0;
}