题目链接:HDU 2196
题目大意:
题目意思大概就是说要求每个树上的节点到其他节点的最远距离。
输入数据:读入一个n,接下来n行,第i行两个数a,b,表示i于a之间连的有一条边权为b的无向边。
题目分析:
因为题目所给N<=10000,所以我们不能用普通的方法,例如floyd,或者对于每一个点求一遍单源最长路。所以正解肯定不是用上述方法做,于是我们再想一想,我们有什么方法?分析题意:他是要求每一个点到其他点的最远距离,我们按照题意画一个图,可以发现在有些点求出来的时候,其他的点也可以求出来。然后如果你对DP比较敏感的话,就可以想到树形DP,再结合求数的直径的思想,这道题就可以做出来了。
正解:
求一个树中所有节点能到达的最远距离f[i]。要用两个dfs。
首先第一个dfs求出所有每个节点i在其子树中的 (这里的正向最大距离应该指的就是从上往下,反向就是从下往上)
正向最大距离和正向次大距离和dist[i][0]和dist[i][1](如果i节点在子树中最大距离经过了2号儿子,那么次大距离就是不经过2号儿子的最大距离)。
并且还要标记longest[i]=j表示节点i在其子树中的最大距离经过了节点j(即j是i的一个儿子)。由上步我们获得了正向最大距离,正向次大距离和最大距离的儿子节点标记。
画图可以知道我们建立的这棵树,i节点的最远距离只有两种选择:
i节点所在子树的最大距离,或者i节点连接它的父节点所能到达的最大距离。
(即前者往下走,后者先往上走之后很可能也往下走)处理dist[i][2]的情况
所以我们只要求出反向最大距离dist[i][2]
(即i节点往它的父节点走所能到达的最大距离)
就可以知道i节点在整个树中能走的最大距离了。dist[i][2]求法:i节点往它的父节j点走,
如果它的父节点的正向最大距离不经过i的话,
那么dist[i][2]要不就是它父节点的反向最大距离+W[i][j]
要不就是它父节点的正向最大距离+ W[i][j]如果它的父节点的正向最大距离经过i的话,那么dist[i][2]
要不就是它父节点的反向最大距离+W[i][j]
要不就是它父节点的正向次大距离+ W[i][j]上面就是dfs2要求的值。最终f[i] = max(dist[i][0],dist[i][2])
以上摘自:http://blog.csdn.net/u013480600/article/details/21831363
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
struct arr{
int nd,nx,co;
}bot[200000];
int n,dist[12000][3],cnt,tot,head[12000],longest[12000];
inline int read(){
int x=0,w=1;char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
return x*w;
}
inline void add(int a,int b,int c) {bot[++cnt].nd=b;bot[cnt].co=c; bot[cnt].nx=head[a]; head[a]=cnt;}
int dfs1(int u,int fa){//dist[i][0,1,2]分别为正向最大距离,正向次大距离,反向最大距离
if(dist[u][0]>=0) return dist[u][0];//返回u的正向最大距离
dist[u][0]=dist[u][1]=dist[u][2]=longest[u]=0;//开始时清0;
for(register int i=head[u];i!=-1;i=bot[i].nx) {
int v=bot[i].nd;
if(v==fa) continue;
int tt=dfs1(v,u);
if(dist[u][0]<tt+bot[i].co) {//如果u的正向最大距离小于u到v的距离+v的正向最大距离
longest[u]=v;
dist[u][1]=max(dist[u][1],dist[u][0]);//u的正向次大距离,因为可以更新u的正向最大距离,所以把原来的正向次大距离和最大距离取一个max
dist[u][0]=tt+bot[i].co;//u的正向最大距离
}
else if(dist[u][1]<tt+bot[i].co) dist[u][1]=tt+bot[i].co;//如果正向最大距离不能更新,那就看能不能更新次最大距离
}
return dist[u][0];//返回值
}
void dfs2(int u,int fa){
for(register int i=head[u];i!=-1;i=bot[i].nx) {
int v=bot[i].nd;
if(v==fa) continue;
//下面就是题解中说的四种情况
if(v==longest[u]) dist[v][2]=max(dist[u][2],dist[u][1])+bot[i].co;
//如果v在u的正向最大距离上,那么v的反向最大距离就是u的反向最大距离或者次最大距离中最大的那个加上u到v的距离
else dist[v][2]=max(dist[u][2],dist[u][0])+bot[i].co;
//如果v不在u的正向最大距离上,那么v的反向最大距离是u的正向或者反向中最大的一个加上u到v的距离
dfs2(v,u);//继续递归下去
}
}
int main(){
while(scanf("%d",&n)==1&&n){
tot=0;
for(register int i=1;i<=cnt;++i) bot[i].nd=bot[i].nx=bot[i].co=0;
for(register int i=1;i<=n;++i) dist[i][0]=dist[i][1]=dist[i][2]=head[i]=longest[i]=-1;//赋一个初始值
for(register int i=2;i<=n;++i) { int v=read(),w=read();add(v,i,w);add(i,v,w); }//加边
dfs1(1,-1);
dfs2(1,-1);
for(register int i=1;i<=n;++i) printf("%d\n",max(dist[i][0],dist[i][2]));
}
}