HDU 2196 Computer(树形DP经典入门题)

本文介绍了如何利用树形动态规划(Tree DP)解决HDU 2196 Computer问题。首先,讨论了暴力求解导致的超时问题,然后详细解释了树形DP的建边方法和DFS过程,包括如何更新每个节点的dp值和sum值。通过举例说明了树形DP的思路,并提供了部分代码片段,帮助理解算法的实现。请注意,此问题涉及多组输入,调试时需特别注意。

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

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=2196

解题思路

先思考一下暴力的方法:

建树(双向建边),如果有n个点,每次选择那个点作为根节点,dfs,记录最深深度

这样的话n个点,每次dfs遍历剩下n-1的点,复杂度O(N²),我试了一下,超时了。

 

于是我去参考了一下大佬的代码,发现了树形DP这样的东西。

 

树形DP做法:

1.先用链式前向星建边

typedef long long ll;
struct node
{
    int to,last;
    ll w,sum;
}edge[20000+5];
int head[10000+5];
int id;

void add(int u,int v,ll w){
    edge[id].to = v; 
    edge[id].last = head[u]; 
    edge[id].w = w; 
    head[u] = id++;
}

edge[i].to记录这条边终点,

edge[i].last记录起点相同的上一条输入的边是edge[某某]   (也就是edge[edge[i].last]),

edge[i].w记录权值

edge[i].sum记录从起点开始沿着这条边延伸最远的距离

 

2.dfs获得根节点(随便找一个,就找1好了)向外扩展的边的sum值

代码:

void dfs(int now,int pre)///pre表示now的父节点,向外拓展,不能回去
{
    for (int i=head[now];i!=0;i=edge[i].last){
        if (edge[i].to!=pre){
            dfs(edge[i].to,now);
            dp[now] = max(dp[now],dp[edge[i].to]+edge[i].w);
            edge[i].sum = edge[i].w + dp[edge[i].to];
        }
    }
}

dp[i] = max(dp[子节点] + 两点之间的w值)

edge[i].sum = dp[边终点] + 边权值

举个例子:

以①为根节点

⑤-④-①-②-③,假设中间每条边权值都为1,dfs求出dp[2] = 1,dp[4] =1,dp[1] = 2(其他两个点dp=0)

不过事实上dp[2] =3,不过dfs只负责向外扩展,正确的dp值在下一步求出向内扩展的值后,比较就能得出(有些点第一遍的dp值可能就是最大的)

3.treedp函数求得正确答案

代码:

主函数

for (int i=head[1];i!=0;i=edge[i].last){
            treedp(1,edge[i].to);
}

treedp函数

void treedp(int now,int son)
{
    ll maxx=0;
    for (int i = head[now];i!=0;i=edge[i].last){
        if (edge[i].to!=son) maxx = max(maxx,edge[i].sum);
    }

    for (int i = head[son];i!=0;i=edge[i].last){
        if (edge[i].to==now) {edge[i].sum = edge[i].w + maxx; break;}
    }

    for (int i = head[son];i!=0;i=edge[i].last){
        dp[son] = max(dp[son],edge[i].sum);
        if (edge[i].to!=now){
        treedp(son,edge[i].to);
        }
    }
}

emm,具体实现自己看吧,怕自己说错误导你们。我还是举个例子说一下自己的理解。

以①为根节点

⑤-④-①-②-③,假设中间每条边权值都为1,还是这个例子吧

第2步不是求出了每条向外拓展的边的sum值嘛,同时dp[2]=1,事实上dp[2]=3

那么怎么求出来呢?第2步我们求出了①->④.sum = 2(向外拓展的),

dp[2]就是①②中间边的权值+2=3 和 原先的 1 取最大值,于是就等于3,同时,②->①.sum = 3

之后继续深搜搞②③。。。emm就这样。

完整代码:(对了这题是多组输入的,不要问本菜鸡为什么debug了一晚上

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define debug(x) printf("----Line #x ----\n",x)
using namespace std;

struct node
{
    int to,last;
    ll w,sum;
}edge[20000+5];
int head[20000+5];
ll dp[10000+5];
int n,id;

void add(int u,int v,ll w){edge[id].to = v; edge[id].last = head[u]; edge[id].w = w; head[u] = id++;}

void dfs(int now,int pre)///pre表示now的父节点,向外拓展,不能回去
{
    for (int i=head[now];i!=0;i=edge[i].last){
        if (edge[i].to!=pre){
            dfs(edge[i].to,now);
            dp[now] = max(dp[now],dp[edge[i].to]+edge[i].w);
            edge[i].sum = edge[i].w + dp[edge[i].to];
        }
    }
}

void treedp(int now,int son)
{
    ll maxx=0;
    for (int i = head[now];i!=0;i=edge[i].last){
        if (edge[i].to!=son) maxx = max(maxx,edge[i].sum);
    }

    for (int i = head[son];i!=0;i=edge[i].last){
        if (edge[i].to==now) {edge[i].sum = edge[i].w + maxx; break;}
    }

    for (int i = head[son];i!=0;i=edge[i].last){
        dp[son] = max(dp[son],edge[i].sum);
        if (edge[i].to!=now){
        treedp(son,edge[i].to);
        }
    }
}

int main()
{
    while (~scanf("%d",&n)){
        ///init
        memset(head,0,sizeof head);
        memset(dp,0,sizeof dp);
        id = 1;

        for (int i=2;i<=n;i++){
            int v;
            ll w;
            scanf("%d %lld",&v,&w);
            add(i,v,w);
            add(v,i,w);
        }
        dfs(1,0);

        for (int i=head[1];i!=0;i=edge[i].last){
            treedp(1,edge[i].to);
        }
/*
        for (int i = 1;i<=n;i++){
            for (int j=head[i];j;j=edge[j].last){
                printf("%d->%d,w=%lld,sum=%lld ",i,edge[j].to,edge[j].w,edge[j].sum);
            }
            printf("dp[%d]=%lld\n",i,dp[i]);
        }
*/
        for (int i=1;i<=n;i++)
            printf("%lld\n",dp[i]);
    }
    return 0;
}

 

基于SpringBoot的在线网络学习平台研究AI更换标第1章引言介绍基于SpringBoot的在线网络学习平台的研究背景、意义、国内外现状、论文研究方法及创新点。1.1研究背景与意义阐述在线网络学习平台的重要性及其在教育领域的应用价值。1.2国内外研究现状分析当前国内外在线网络学习平台的发展状况及趋势。1.3研究方法与创新点说明本研究采用的方法论和在研究过程中的创新之处。第2章相关理论技术概述SpringBoot框架、在线教育理论及相关技术基础。2.1SpringBoot框架概述介绍SpringBoot框架的特点、优势及其在Web应用中的作用。2.2在线教育理论阐述在线教育的基本理念、教学模式及其与传统教育的区别。2.3相关技术基础介绍开发在线网络学习平台所需的关键技术,如前端技术、数据库技术等。第3章在线网络学习平台设计详细描述基于SpringBoot的在线网络学习平台的整体设计方案。3.1平台架构设计给出平台的整体架构图,并解释各个模块的功能及相互关系。3.2功能模块设计详细介绍平台的主要功能模块,如课程管理、用户管理、在线考试等。3.3数据库设计说明平台的数据库设计方案,包括数据表结构、数据关系等。第4章平台实现与测试阐述平台的实现过程及测试方法。4.1平台实现详细介绍平台的开发环境、开发工具及实现步骤。4.2功能测试对平台的主要功能进行测试,确保功能正常且符合预期要求。4.3性能测试对平台的性能进行测试,包括响应时间、并发用户数等指标。第5章平台应用与分析分析平台在实际应用中的效果及存在的问,并提出改进建议。5.1平台应用效果介绍平台在实际教学中的应用情况,包括用户反馈、使用情况等。5.2存在问及原因分析分析平台在运行过程中出现的问及其原因,如技术瓶颈、用户体验等。5.3改进建议与措施针对存在的问提出具体的改进建议和措施,以提高平台的性能和用户满意度
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值