【树形DP】氪金带东

[][] [][] [][] [][] [][] [][] [][] [][] [][] [][] [][] [][] [][] [][] [][]

问题描述

实验室里原先有一台电脑(编号为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

Sample Input

5
1 1
2 1
3 1
1 1

Sample Output

3
2
3
4
4

思路

经典的树上dp问题。传统的求法是以每个点为根计算最大深度,复杂度为n2。但实际上我们只需要默认一个根,其余节点通过拐点的方式,即路径经过父节点,来求最长路径。对于每个节点,最长路径只有两种情况,子树中最长路和经过父节点的最长路。那么DP[v],DP[u]有什么关系?显然v的经过父节点的最长路径由u经过父节点的最长路径和u中其他最长路径决定。
若dp[i]代表拐点最长路径,则动态转移方程式为dp[v]=max(p[v],dp[u]),其中u为v的父节点,p[v]代表u子树中除含v子路径之外的最长路加上u->v的长度。
接下来求解父亲子树中除v之外的最长路:维护每个子树的最大和次大深度(可以相同),则如果v的深度为最大深度,那么其他节点的最大深度为次大深度,否则其他节点的最大深度就是整个子树的最大深度。
之后从根节点开始dp即可。
复杂度是O(n)。

总结

!本题最大的坑点居然是输入多组数据,,,所以说完完整整的看一遍题是多么重要。
!注意vector下标从0开始。
!树中DP方向可能自顶向下也可能自底向上。

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define mem(a,s) memset(a,s,sizeof(a))
int n;
struct edge{
	int v,w;
};
vector<edge> G[10010];                             
int d[10010];//当前节点子树最大深度 
int dx[10010];//父子树中除了当前子节点的最大深度 
int father[10010];//父节点 
int l[10010];//与父节点连接的边的长度 
int dfs(int u){//求最大深度 
    int maxn=0,maxnn=0;
	for(int i=0,len=G[u].size();i<len;i++){//节省时间的for写法 
		int v=G[u][i].v;
		if(father[u]!=v){
			father[v]=u;
			l[v]=G[u][i].w;
			if(maxn<dfs(v)+l[v])
				maxnn=maxn,maxn=d[v]+l[v];
			else if(maxnn<d[v]+l[v])
				maxnn=d[v]+l[v];//维护最大和次大元素 
		}
	}
	for(int i=0,len=G[u].size();i<len;i++){
		int v=G[u][i].v;
		if(maxn==d[v]+l[v])//最大元素:次大 
			dx[v]=maxnn; 
		else dx[v]=maxn; 
	}//其他节点的最大长度 
	d[u]=maxn;
	return d[u];//叶子节点返回0 
}
int dp[10010];
void DFS(int u,int L){
	for(int i=0,len=G[u].size();i<len;i++){
		int v=G[u][i].v;
		if(father[u]==v) continue; 
		int LL=max(dx[v],L)+l[v];
		DFS(v,LL);//绕行最大长度
		dp[v]=max(d[v],LL);//和深度比较 
	}
}
void add_edge(int u,int v,int w){G[u].push_back({v,w});}//加入单向边u->v
int main(){
	//freopen("1.txt","r",stdin);
	while(~scanf("%d",&n)){
		rep(i,1,n)
			G[i].clear();
		rep(i,2,n){
			int w,v;
			scanf("%d%d",&v,&w);
			add_edge(i,v,w);add_edge(v,i,w);
		}
		father[1]=1;dfs(1);
		dp[1]=d[1];DFS(1,0);
		rep(i,1,n)
			printf("%d\n",dp[i]); 
	}

	return 0;
}

发布了29 篇原创文章 · 获赞 1 · 访问量 481
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览