HDU 2196 树形dp

本文介绍了一种使用树形动态规划解决寻找每个节点到其他节点最大边权值的问题。通过两次深度优先搜索(DFS),分别计算了从子树出发的最大及次大边权值,以及从父节点出发的最大边权值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先不得不说这题真的很不错 花了我一整天的时间来想= =


题目要求大概就是给你一棵树,求每一个节点到达的其他点所经过边的权值的最大值

开始还想用FLOYD 但是看到10000个结点= =顿时打消了念头

后来想到应该树形dp 

对于一个结点,他能取得的最大值有2种可能,1是从他的子树走,2是从父节点走

所以

用far数组表示一个节点从他的子树走所能到达的最大权值

second数组表示次远值(这个数组表示的边不能与far数组表示的边有交集)

max_v数组表示取得far数组值时该节点走向的子树的根节点

from_fathershu数组表示从父亲节点走所能取得的最大权值


比如一棵树

                1

       (2) /       \ (1)

          2          4           

   (1) /              \(1)

       3                 5

括号表示那条边的权值

这棵树中far[1] = 3(1-2-3这条路径最大),second[1] = 2(不能走1-2-3这条路径,只能从另外一边走,如果没有的话另一条边那就为0)

max_v[1] = 2(从以2为根这棵子树走的时候取得最大值),from_father[1] = 0(没有父亲结点,所以为0)from_father[4] = 4(4-1-2-3取得最大值)


然后dfs2次,第一次先求出far和second状态转移方程很好写

far[i] = max(far[i],son[i]+val[i,son[i]]);(val[i,son[i]]表示i和son[i]相连这条边的权值)

second[i]就在求far的过程中动态选择 直接看代码吧

max_v在求far的时候也很容易就拿到了


第二次dfs,我们求from_father数组

状态转移方程

if(j是i的儿子){

           if(j==max_v[i]) from_father[j] = max(from_father[i],second[i])+val[i,j];(这时候,j在i取得最大权值的那条路上,所以不能从取得最大权值的那条路走,只能从次大权值路走,还可能继续由父节点的父节点走)

           else from_father[j] = max(from_father[i],far[i])+val[i,j];(这时候,可能由父节点的父节点走,也可能由父节点的最大权值路走)


下面是代码


#include <iostream>

#include <vector>

#include <string.h>


using namespace std;


int n;

vector<int> son[10001];//结点的儿子

int val[10001];

int far[10001],second[10001],max_v[10001],from_father[10001];


void dfs(int u)

{

    max_v[u] = u;

    for(int i = 0; i<son[u].size(); i++)

    {

        int v = son[u][i];

        dfs(v);

        if(far[u]<far[v]+val[v])

        {

            second[u] = far[u];

            far[u] = far[v]+val[v];

            max_v[u] = v;

        }

        else if(far[v]+val[v]>second[u])

            second[u] = far[v]+val[v];

    }

}


void dfs2(int u)

{

    for(int i = 0; i<son[u].size(); i++)

    {

        int v = son[u][i];

        if(v==max_v[u]) from_father[v] = max(from_father[u],second[u])+val[v];

        else  from_father[v] = max(from_father[u],far[u])+val[v];

        dfs2(v);

    }

}


int main(void)

{

    cin.sync_with_stdio(false);

    cout.sync_with_stdio(false);

    while(cin>>n)

    {

        for(int i = 0; i<=n; i++)

        {

            son[i].clear();

        }

        int a,b;

        for(int i = 2; i<=n; i++)

        {

            cin>>a>>b;

            son[a].push_back(i);

            val[i] = b;

        }

        val[1] = 0;

        memset(far,0,sizeof(int)*(n+1));

        memset(second,0,sizeof(int)*(n+1));

        memset(from_father,0,sizeof(int)*(n+1));

        dfs(1);

        dfs2(1);

        for(int i = 1; i<=n; i++) cout<<max(far[i],from_father[i])<<endl;

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值