HDU 2196 Computer

http://acm.hdu.edu.cn/showproblem.php?pid=2196

#include<iostream>
using namespace std;
#define N 10001
struct node{
	int next,v,w;
	node(){};
	node(int a,int b,int c){
		next=a;v=b;w=c;
	}
}E[N];
int head[N],NE;
int up[N],down_f[N],down_s[N];
int mark[N];
void init(){
	NE=0;
	memset(head,-1,sizeof(head));
}
void insert(int u,int v,int w){
	E[NE]=node(head[u],v,w);
	head[u]=NE++;
}
int dfs(int u){ //从下往上跟新 
	int M1=-1,M2=-1,id;
	bool flag=0;
	for(int i=head[u];i!=-1;i=E[i].next){
		flag=1;
		int v=E[i].v;
		int w=dfs(v);
		if(w+E[i].w>M1){
			M2=M1;
			M1=E[i].w+w;
			id=i;			
		}
		else if(w+E[i].w==M1)
			M2=M1;
		else if(w+E[i].w>M2)
			M2=w+E[i].w;
	}
	if(!flag){
		down_f[u]=0;
		down_s[u]=0;
	}
	else{
		down_f[u]=M1;
		mark[u]=id;
		down_s[u]=M2;
	}
	return down_f[u];
}
void dfs2(int u){ //从上往下跟新 
	for(int i=head[u];i!=-1;i=E[i].next){
		int v=E[i].v;
		if(i==mark[u])
			up[v]=max(up[u],down_s[u])+E[i].w;
		else
			up[v]=max(up[u],down_f[u])+E[i].w;
		dfs2(v);
	}
}
int GetInt(){
    char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    int num=0;
    while(ch>='0'&&ch<='9'){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num;
}
int main(void){
	int n;
	while(~scanf("%d",&n)){
		init();
		for(int i=2;i<=n;i++){
			int u,w;
			u=GetInt();
			w=GetInt();
			insert(u,i,w);
		}
		dfs(1);
		up[1]=0;
		dfs2(1);
		for(int i=1;i<=n;i++)
			printf("%d\n",max(down_f[i],up[i]));
	}
}
树形DP啊,很弱,这个经典题,用到的dp数组不止一个

这道题就是给你一颗树,问你树中每个节点到任意节点的最长距离

首先看一下这个最长距离可能由哪些途径来"竞争"! 根节点不用说,就是到最远的叶子节点距离,主要是除了根节点以外的节点

1.对于叶子节点,在图上随意画个树,可以发现,叶子节点的最远距离由它的父亲节点得到,可能是父亲节点往上走,走到根,然后加上根节点到其他叶子节点的最远距离;也有可能是父亲节点往下走,也许这个父亲节点另外一个孩子路径节点特别多,那么这个时候最远距离就是它了。问题在于,只记录一个节点往下的最大距离就够了吗?答案是不行!因为要是你这个最大距离包含着当前点怎么办?这样就重合了,解决的办法就是记录一个次最远距离,当重合的时候,就用这个次最远距离就好了,dfs中就是处理最远距离和次远距离的,同时要记录以当前节点为根的最远距离的编号,用来判断之后是否重合

2.对于非叶子节点,它有可能是往下走,这就是down_f[]中记录的值,也有可能是往上走,跟叶子节点一样

用到三个dp数组,down_f[]记录该节点往下走的最远距离,down_s[]记录次远距离,up[]记录往上走,走到根又往下走的最远距离

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值