Week6 作业A - 氪金带东

1.题意

实验室里原先有一台电脑(编号为1),最近氪金带师咕咕东又为实验室购置了N-1台电脑,编号为2到N。每台电脑都用网线连接到一台先前安装的电脑上。但是咕咕东担心网速太慢,他希望知道第i台电脑到其他电脑的最大网线长度,但是可怜的咕咕东在不久前刚刚遭受了宇宙射线的降智打击,请你帮帮他。
在这里插入图片描述
提示: 样例输入对应这个图,从这个图中你可以看出,距离1号电脑最远的电脑是4号电脑,他们之间的距离是3。 4号电脑与5号电脑都是距离2号电脑最远的点,故其答案是2。5号电脑距离3号电脑最远,故对于3号电脑来说它的答案是3。同样的我们可以计算出4号电脑和5号电脑的答案是4.

2.样例

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

3.解题思路

  1. 该题大意就是对于一棵n个结点的无根树,求出每个结点到最远点的距离,要求时间复杂度为O(n)
  2. 该题转化为,对于一个点距离它最远的点一定是直径的一个端点,找树的直径的端点,方法:两次dfs即先从任意一点v1出发,找到距它最远的点v2,再从v2出发,找到距它最远的点v3,则v2,v3为直径的两个端点,v2到v3的距离为直径
    证明:
      (1)若v1为直径上的一点,则v2也在直径上且为直径的端点
      (2)若v1不在直径上,用反证法证明,设此时v2v3不是直径,vu是直径。
      v2v3与vu有交点o(因为电脑间是连接的且直径最长),因为v2距v3最远,所以v2o+ov3>v2o+ov,所以ov3>ov,则uo+ov3>uo+ov=uv,所以uv不是直径,即v2为直径的一个端点
      (3)综上,从任意一点v1出发,找到距它最远的点v2,则v2为直径的一个端点,同一方法可以得到直径的另一个端点
    在这里插入图片描述
  3. 用dis记录路径长度,用l_p记录最后一个端点,当路径更长时更新l_p,注意每次调用dfs要清空dis,l_p,vis,在每组数据完成后清空vector。

5.AC代码

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=1e4+5;
struct edg{
	int v;
	long long w;
	edg(int a,long long b){
		v=a;
		w=b;
	}
};
vector<edg>e[M];
long long max_dis[M][2];
long long dis;
int l_p;
int vis[M];
void dfs(int u,long long ds,int f)
{
	vis[u]=1;
	max_dis[u][f]=ds;
	if(ds>dis)
	{
		dis=ds;
		l_p=u;
	}
	for(int i=0;i<e[u].size();i++)
	{
		int v=e[u][i].v;
		long long w=e[u][i].w;
		if(vis[v]==0)
		{
			dfs(v,ds+w,f);
		}
	}
}
int main()
{
	int n;
	while(~scanf("%d",&n))
	{
		int v1,l_p1=1,l_p2=1;
		long long w1;
		memset(vis, 0, sizeof(vis));
		for(int i=0;i<M;i++)
		{
			for(int j=0;j<2;j++)
			{
				max_dis[i][j]=0;
			}
		}
		for(int i=2;i<n+1;i++)
		{
			cin>>v1>>w1;
			e[i].push_back(edg(v1,w1));
			e[v1].push_back(edg(i,w1));
		}
		l_p=0;
		dis=0;
		dfs(1,0,0);
		l_p1=l_p;
		memset(vis, 0, sizeof(vis));
		l_p=0;
		dis=0;
		dfs(l_p1,0,0);
		l_p2=l_p;
		memset(vis, 0, sizeof(vis));
		dfs(l_p2,0,1);
		for(int i=1;i<=n;i++)
		{
			cout<<max(max_dis[i][0],max_dis[i][1])<<endl;
		}
		for(int i=0;i<M;i++)
		{
			while(!e[i].empty())
			{
				e[i].clear();
			}
		}
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值