树形DP + 节点到达的最长距离
题意:
给出一棵树,问每一个节点能到的最远的距离。
思路:
题目很简单,意思也很明白,但是如果是想求出一个节点到其它点的最远的距离,只需一次dfs就能知道,此题需要n次dfs,所以还需其它更快的算法。
首先思考一个节点的最远的距离怎么算出?
最远的距离只有两种可能:
1. 此节点的儿子在最远的距离上
2. 最远距离经过父亲节点
第一种很容易算出,而经过父亲节点最远距离有两种,一种是经过了父亲的其它儿子,一种是经过了父亲的的父亲。所以需要保存这两种情况,第一种可以保存父亲节点的经过儿子的最远距离和次远距离,当父亲的经过儿子的最远距离不经过当前儿子,那么就可以用父亲经过儿子的最远距离,所过经过了儿子的最远距离经过了当前节点,那么就是用次远距离。(所谓的次远距离就是不经过最远儿子距离的经过其它儿子的最远距离)
所以,三者都需要算出。定义:dp[u][0/1/2] 表示到达u点的最远距离,0表示经过儿子节点的最远距离1,1表示经过其它儿子的最远距离,2表示经过父亲节点的最远距离。
dp[u][0/1] 很容易算出,而dp[u][2] 可以通过dfs递推算出。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 100005;
int n;
int dp[maxn][3];
int head[maxn],pos;
int flag[maxn];
struct Node
{
int e,v;
int next;
}edge[maxn*2];
void init()
{
pos = 0;
memset(head,-1,sizeof(head));
}
void input()
{
for(int i = 2;i <= n; i++) {
int b,c;
scanf("%d%d",&b,&c);
edge[pos].e = b;
edge[pos].v = c;
edge[pos].next = head[i];
head[i] = pos++;
edge[pos].e = i;
edge[pos].v = c;
edge[pos].next = head[b];
head[b] = pos++;
}
}
void dfs1(int u,int fa)
{
dp[u][0] = dp[u][1] = dp[u][2] = flag[u] = 0;
for(int i = head[u];i != -1; i = edge[i].next) {
int to = edge[i].e;
if(to == fa) continue;
dfs1(to,u);
if(dp[u][0] < dp[to][0] + edge[i].v) {
flag[u] = to;
dp[u][1] = max(dp[u][1],dp[u][0]);
dp[u][0] = dp[to][0] + edge[i].v;
}
else
dp[u][1] = max(dp[u][1],dp[to][0] + edge[i].v);
}
}
void dfs2(int u,int fa)
{
for(int i = head[u];i != -1;i = edge[i].next) {
int to = edge[i].e;
if(to == fa) continue;
if(flag[u] == to)
dp[to][2] = max(dp[u][2],dp[u][1]) + edge[i].v;
else
dp[to][2] = max(dp[u][2],dp[u][0]) + edge[i].v;
dfs2(to,u);
}
}
int main(int argc, char const *argv[])
{
//freopen("in.txt","r",stdin);
while(scanf("%d",&n) != EOF) {
init();
input();
dfs1(1,-1);
dfs2(1,-1);
for(int i = 1;i <= n; i++)
printf("%d\n",max(dp[i][0],dp[i][2]));
}
return 0;
}