树形dp hdu 2196

题目链接:hdu 2196

题目大意:有n台电脑,每台电脑之间都用电缆相连,当然了这是n-1边树。然后问每台电脑所能到达的最远电脑的距离。

思路:首先第一反应思路有点像单源最长路径。。。但是有n台电脑,都来一发估摸着n^2logn的时间复杂度(果断t了)。后来想想又有点树的分治的感觉,分枝节处理,,,,然后dp优化。。。就可以了。。。。。。。。。。。。。。来一发dfs找到离1(之所以选1是因为n至少为1,所以1节点是肯定有的)节点的最远距离,这个过程中可以用dp数组来记录从叶子节点回溯的最远距离,顺便用边的dis记录经过这条边的最远距离,因为路径是双向的所以特别注意双向边处理。。第二发dfs的时候就是找到不经过当前子节点的父节点到其他子节点的最远距离为now,然后与当前节点的dp值取最大即可。。当沿着路径求dp值时,把pa->son,son->pa的边值dis修改为now,再处理son的子节点就不用求pa->son这条路径的最远距离了。。。。。。。。。。估计看到这大家都凌乱了,,看代码吧。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/**************************************************************
    Problem:hdu 2196
    User: youmi
    Language: C++
    Result: Accepted
    Time:46MS
    Memory:2056K
****************************************************************/
//#pragma comment(linker, "/STACK:1024000000,1024000000")
//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <stack>
#include <sstream>
#include <cmath>
#include <queue>
#include <string>
#include <vector>
#define zeros(a) memset(a,0,sizeof(a))
#define ones(a) memset(a,-1,sizeof(a))
#define Max(a,b) (a)>(b)?(a):(b)
#define Min(a,b) (a)<(b)?(a):(b)
#define sc(a) scanf("%d",&a)
#define sc2(a,b) scanf("%d%d",&a,&b)
#define rep0(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define rep_0(i,n) for(int i=n-1;i>=0;i--)
#define rep_1(i,n) for(int i=n;i>=1;i--)
#define pt(a) printf("%d\n",a)
#define lson (step<<1)
#define rson (lson+1)
#define esp 1e-6
#define oo 0x3fffffff
#define TEST cout<<"*************************"<<endl

using namespace std;
typedef long long ll;
int n;
const int maxn=10000+10;
struct side
{
    int v,w,next,dis;
}e[maxn<<1];
int T;
int head[maxn];
int vis[maxn];
int dp[maxn];
void init()
{
    T=0;
    zeros(dp);
    zeros(vis);
    ones(head);
}
void build(int u,int v,int w)
{
    e[T].v=v;
    e[T].w=w;
    e[T].next=head[u];
    head[u]=T++;
}
void dfs(int u)
{
    vis[u]=1;
    for(int i=head[u];~i;i=e[i].next)
    {
        int v=e[i].v;
        if(!vis[v])
        {
            dfs(v);
            dp[u]=Max(dp[u],dp[v]+e[i].w);
            e[i].dis=dp[v]+e[i].w;
            if(i%2==1)//因为是无向边,所以修改边值时两天边都要处理
                e[i-1].dis=e[i].dis;
            else
                e[i+1].dis=e[i].dis;
        }
    }
}
void tree_dp(int ps,int pa,int son)
{
    vis[pa]=1;
    int now=0;
    int temp=e[ps].dis;
    //printf("pa->son %d->%d ps(%d)->%d\n",pa,son,ps,temp);
    for(int i=head[pa];~i;i=e[i].next)//找到pa节点到除son节点以外的其他儿子节点的最大值
    {
        int v=e[i].v;
        if(v!=son)
        {
            //printf("pa->son %d->%d\n",pa,v);
            //pt(e[i].dis);
            now=Max(now,e[i].dis+e[ps].w);
            //pt(now);
        }
    }
    if(now==0)//当要继续向下修改子节点的dp值时,改变pa->son,son->pa这条双向边的值,当son的儿子节点访问到这条边时就可以直接求出经过son这个点的最远距离
        now+=e[ps].w;
    if(ps%2==1)
        e[ps-1].dis=e[ps].dis=now;
    else
        e[ps+1].dis=e[ps].dis=now;
    dp[son]=Max(now,dp[son]);
    //printf("ps->%d dis->%d pa->%d son->%d now->%d dp->%d\n",ps,e[ps].dis,pa,son,now,dp[son]);
    for(int i=head[son];~i;i=e[i].next)
    {
        int v=e[i].v;
        if(!vis[v])
        {
            tree_dp(i,son,v);
        }
    }
    if(ps%2==1)//处理完儿子节点以后要记得把边值修改回来,因为pa->其他儿子节点的最远距离需要访问的时第一遍dfs是的dp值,也就是第一遍dfs时的最远距离
        e[ps-1].dis=e[ps].dis=temp;
    else
        e[ps+1].dis=e[ps].dis=temp;
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(~sc(n))
    {
        int v,w;
        init();
        for(int i=2;i<=n;i++)
        {
            sc2(v,w);
            build(i,v,w);//建立无向边,两天边的大小关系是前者比后者边的id小1,模拟一下。比如第一条边和第二条边的id分别为0,1
            build(v,i,w);
        }
        dfs(1);
        zeros(vis);
        for(int i=head[1];~i;i=e[i].next)
        {
            int v=e[i].v;
            tree_dp(i,1,v);
        }
        rep1(i,n)
            pt(dp[i]);
    }
    return 0;
}

 

你可能感兴趣的:(HDU)