HDU 2196 Computer
题目大意:一开始有一台电脑编号为1,然后接下来把剩下的电脑全连进去,每一行给出该台电脑的父亲编号和连接的权值,问最后每台电脑能到达最远的权值为多少。
分析:乍一看,很容易想到用dfs直接搜,然后分析对于每台电脑,我们将m电脑连入n电脑,相当于指向了n电脑,然后其他的电脑又可能连向m,发现每个点有出度和入度。那么我们就能想到他能到达的最远距离一定是出度的最远距离和入度的最远距离。
我们先来分析入度的最远距离,就是作为树的根对子节点进行dfs,可以得到当该电脑作为根的时候,能够到达最远的距离是多少。
再来分析出度的最远距离,当该节点作为儿子v,那么我们就来找与他相连的点中,有没有做过他最优的父节点,如果是,那么就是父节点u的最远出度距离或者是u的最远入度距离加上两个点之间的权值。这个时候发现一个问题,就是如果我之前v出现在u的最远入度距离上,那么显然不能够再次被用,如果直接取u的出度最远距离,那么可能会出错。怎么解决这个问题呢,就是在计算最远入度距离的时候算上 次最远入度距离,和最远出度距离作比较。
我们把dp[u][0] ,dp[u][1],dp[u][2] 分别代表每个点的最远入度,次入度,出度距离。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxx = 1e4+10;
int dp[maxx][3];
int head[maxx];
int index[maxx];
int tot;
struct node
{
int v;
int nxt;
int w;
}edge[2*maxx];
void add(int x,int y,int w) //链式前向星存图
{
edge[tot].v = y;
edge[tot].nxt = head[x];
edge[tot].w = w;
head[x] = tot++;
}
void dfs_down(int u,int fu)
{
int mxx=0,mx=0;
for(int i = head[u];~i;i = edge[i].nxt){
int v = edge[i].v;
if(v==fu) continue;
dfs_down(v,u);
if(mxx<=dp[v][0]+edge[i].w){ //'<='包含了情况dp[u][0] == dp[u][1]
mx = mxx;
mxx = dp[v][0]+edge[i].w;
index[u] = v;
}
else if(mx<dp[v][0]+edge[i].w) mx = edge[i].w+dp[v][0];
else if(mx<dp[v][1]+edge[i].w) mx = edge[i].w+dp[v][1];
}
dp[u][0] = mxx;
dp[u][1] = mx;
return ;
}
void dfs_up(int u,int fu)
{
for(int i = head[u];~i;i=edge[i].nxt){
int v = edge[i].v;
if(v==fu) continue;
if(index[u]==v) {
dp[v][2] = max(dp[u][1]+edge[i].w,dp[u][2]+edge[i].w); //说明在u出度最长线上
}
else dp[v][2] = max(dp[u][0]+edge[i].w,dp[u][2]+edge[i].w);
dfs_up(v,u);
}
return ;
}
int main ()
{
int n,x,y;
while(~scanf("%d",&n)){
tot = 0;
memset(dp,0,sizeof(0));
memset(head,-1,sizeof(head));
for(int i=2;i<=n;i++){
scanf("%d%d",&x,&y);
add(i,x,y);
add(x,i,y);
}
dfs_down(1,1);
dfs_up(1,1);
for(int i=1;i<=n;i++)
printf("%d\n",max(dp[i][0],dp[i][2]));
}
return 0;
}