树形DP HDU 2196

题目链接: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]));
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值