codevs 1814 最长链题解

博客围绕codevs 1814最长链问题展开,给出二叉树结点数及各结点左右儿子编号的输入规则,要求输出最长链长度。介绍了数据规模,解释了二叉树和最长链概念,解析中指出这是求最长链模板题,可通过前向星连边、两次DFS求解,还提醒数组要开大。

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

codevs 1814 最长链题解

题目描述 Description

现给出一棵N个结点二叉树,问这棵二叉树中最长链的长度为多少,保证了1号结点为二叉树的根。

输入描述 Input Description

输入的第1行为包含了一个正整数N,为这棵二叉树的结点数,结点标号由1至N。

接下来N行,这N行中的第i行包含两个正整数l[i], r[i],表示了结点i的左儿子与右儿子编号。如果l[i]为0,表示结点i没有左儿子,同样地,如果r[i]为0则表示没有右儿子。

输出描述 Output Description

输出包括1个正整数,为这棵二叉树的最长链长度。

样例输入 Sample Input

5

2 3

4 5

0 6

0 0

0 0

样例输出 Sample Output

4

数据范围及提示 Data Size & Hint

【样例说明】

  4-2-1-3-6为这棵二叉树中的一条最长链。

 

【数据规模】

对于10%的数据,有N≤10;

对于40%的数据,有N≤100;

对于50%的数据,有N≤1000;

对于60%的数据,有N≤10000;

对于100%的数据,有N≤100000,且保证了树的深度不超过32768。

 

【提示】

关于二叉树:

二叉树的递归定义:二叉树要么为空,要么由根结点,左子树,右子树组成。左子树和右子树分别是一棵二叉树。

请注意,有根树和二叉树的三个主要差别:

1. 树的结点个数至少为1,而二叉树的结点个数可以为0;

2. 树中结点的最大度数没有限制,而二叉树结点的最大度数为2;

3. 树的结点无左、右之分,而二叉树的结点有左、右之分。

关于最长链:

最长链为这棵二叉树中一条最长的简单路径,即不经过重复结点的一条路径。可以容易证明,二叉树中最长链的起始、结束结点均为叶子结点。

解析:

寻求最长链的模板题.

输入时要注意将其父节点和子节点进行前向星连边,边权都设为1.

随机找一个点,其实也就是根节点1,然后就跑一遍DFS,寻求最大值,并且记录下该最大值的标号.然后再从此标号跑一遍DFS,最后得到一个最大值,即为所求.

数组一定要开大!!!!

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cmath>
 4 #include <cstring>
 5 #include <cstdlib>
 6 #include <queue>
 7 #include <stack>
 8 #include <algorithm>
 9 #define Max 1000012
10 #define re register
11 struct tree
12 {
13     int next,to,dis;
14 }t[Max];
15 int cnt,n,ans=0,node,head[Max],dis[Max];
16 bool vis[Max]={0};
17 void add(int u,int v,int w)
18 {
19     t[++cnt].next=head[u];
20     t[cnt].to=v;
21     t[cnt].dis=1;
22     head[u]=cnt;
23 }
24 void dfs(int x,int p)
25 {
26     for(re int i = head[x] ; i ; i = t[i].next) {
27         int v = t[i].to;
28         if(vis[v]) continue;
29         vis[v]=1;
30         dis[v] = p + t[i].dis;
31         if(dis[v] > ans) {
32             ans=dis[v];
33             node=v;
34         }
35         dfs(v,dis[v]);
36     }
37 }
38 int main()
39 {
40     scanf("%d",&n);
41     int x,y;
42     for(re int i = 1 ; i <= n ; ++ i) {
43         scanf("%d%d",&x,&y);
44         if(y!=0)
45             add(i,y,1),add(y,i,1);
46         if(x!=0)
47             add(i,x,1);add(x,i,1);
48     }
49     memset(dis,0,sizeof dis);
50     dfs(1,0);
51     memset(vis,0,sizeof vis);
52     ans=0;
53     dfs(node,0);
54     printf("%d",ans);
55     return 0;
56 }
AC代码

 

转载于:https://www.cnblogs.com/handsomegodzilla/p/11185493.html

内容概要:本文档提供了关于“微型车间生产线的设计与生产数据采集试验研究”的毕业设计复代码,涵盖从论文结构生成、机械结构设计、PLC控制系统设计、生产数据采集与分析系统、有限元分析、进度管理、文献管理和论文排版系统的完整实。通过Python代码和API调用,详细展示了各个模块的功能实和相互协作。例如,利用SolidWorks API设计机械结构,通过PLC控制系统模拟生产流程,使用数据分析工具进行生产数据的采集和异常检测,以及利用进度管理系统规划项目时间表。 适合人群:具有机械工程、自动化控制或计算机编程基础的学生或研究人员,尤其是从事智能制造领域相关工作的人员。 使用场景及目标:①帮助学生或研究人员快速搭建和理解微型车间生产线的设计与实;②提供完整的代码框架,便于修改和扩展以适应不同的应用场景;③作为教学或科研项目的参考资料,用于学习和研究智能制造技术。 阅读建议:此资源不仅包含详细的代码实,还涉及多个学科领域的知识,如机械设计、电气控制、数据分析等。因此,在学习过程中,建议读者结合实际操作,逐步理解每个模块的功能和原理,并尝试调整参数以观察不同设置下的系统表。同时,可以参考提供的文献资料,深入研究相关理论和技术背景。
本次的学生体质健康信息管理网站,按照用户的角色可以分为教师与学生,后台设置管理员角色来对学生的信息进行管理。,设计如下: 1、后台管理系统 后台管理系统主要是为该系统的管理员提供信息管理服务的系统,具体包括的功能模块如下: (1)管理员信息管理 (2)教师信息管理 (3)学生信息管理 (4)健康信息统计(图形化进行健康,亚健康等学生的信息数量统计) 2、教师角色的功能模块设计 教师角色所需要的功能模块主要包括了如下的一些内容: (1)个人资料修改 (2)学生体质健康管理:录入相关数据,包括但不限于身高、体重、肺活量、视力等生理指标以及运动能力、身体成分、骨密度等健康指标,并且设置健康,亚健康状态 (3)学生健康建议:根据体质信息,进行学生健康的建议 (4)健康预警:对健康出题的学生,进行健康预警 (5)饮食和锻炼情况管理,查看 3、学生角色 学生角色可以通过该信息网站看到个人的基本信息,能够看到教师给与学生的健康建议等,功能模块设计如下: (1)个人资料修改 (2)我的健康建议查看 (3)我的健康预警 (4)饮食和锻炼情况管理,记录平时的饮食和锻炼情况 完整前后端源码,部署后可正常运行! 环境说明 开发语言:Java后端 框架:ssm,mybatis JDK版本:JDK1.8+ 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:eclipse/idea Maven包:Maven3.3+ 部署容器:tomcat7.5+
<think>首先,用户的题是关于计算树中最长路径的节点权重之和。路径是节点序列,每个节点有一个权重,我需要找到权重和最大的路径。 关键点: - 树是无环连通图,所以没有环。 - 路径可以是任意两个节点之间的路径。 - 我需要最大化节点权重之和。 在树中,最长路径(权重和最大)通常称为"diameter",但diameter通常指边的数量最多,这里是节点权重之和最大。所以,我需要处理带权节点。 从引用中: - 引用[1]:提到了二叉树的最长路径长度和最大路径和。公式:sum(high_node) = max(sum(left), sum(right)) + value,以及路径和 path_sum = sum(left) + sum(right) + value。这似乎是针对二叉树,计算从根到叶的路径或类似。 - 引用[2]:讨论了最短和最长路径算法,提到了负权重和NP难题,但树中没有环,所以可能不是NP难题。 对于树,由于没有环,我们可以用动态规划或DFS来高效解决。 算法思路: 1. 对于树,任意两个节点之间的路径是唯一的。 2. 最长路径(最大权重和)可能涉及两个叶子节点或通过根节点。 3. 一个常见的方法是:对于每个节点,计算以该节点为根的子树中,从该节点到叶子节点的最大权重和路径(单一路径)。 4. 同时,计算以该节点为顶点的路径的最大权重和,这可以是左子树最大路径 + 节点权重 + 右子树最大路径(对于二叉树)。 5. 然后,全局最大值就是树中最大路径和。 对于一般的树(不一定是二叉树),类似: - 我们可以用DFS遍历树。 - 对于每个节点u,维护两个值: - max_single[u]:以u为起点的向下路径(到叶子)的最大权重和。 - max_path[u]:以u为顶点的路径的最大权重和(即路径在u处转折)。 - 然后,全局最大路径和是max over all u of max_path[u]。 步骤: 1. 从根节点开始DFS。 2. 对于叶节点:max_single[leaf] = weight[leaf],max_path[leaf] = weight[leaf](因为只有一个节点)。 3. 对于内部节点u: - 计算所有子节点v的max_single[v]。 - max_single[u] = weight[u] + max( max_single[v] for v in children ),或者如果所有子树的max_single[v]都负,可能只取weight[u]。 - max_path[u] = weight[u] + sum of two largest max_single[v] for v in children(如果有两个或更多孩子),或者如果只有一个孩子,max_path[u] = weight[u] + max_single[v](但路径需要两个方向,所以可能不准确)。 - 更精确:max_path[u] 表示以u为顶点的路径的最大和,即路径从u的一个子树下来,经过u,再到另一个子树。所以对于u,取两个最大的max_single[v]值,加上weight[u]。 - 如果只有一个孩子,那么以u为顶点的路径只能是u到叶子,但这不是一个“转折”路径;然而,全局最大路径可能只是单一路径。 - 因此,max_path[u] 应该包括所有可能:单一路径或转折路径。 - 定义:max_path[u] 是子树u中以某节点为顶点的路径的最大和(包括单一路径)。 - 所以,max_path[u] = max( weight[u] + max_single[v] for some v, # 单一路径 weight[u] + max_single[v1] + max_single[v2] for v1 != v2 # 转折路径 ) - 但由于路径可能不经过u,但在子树中,所以我们需要比较: - 候选1:以u为端点的单一路径:weight[u] + max( max_single[v] for v in children ) 或者 weight[u] 如果无孩子 - 候选2:以u为顶点的转折路径:如果有至少两个孩子,weight[u] + max_single[v1] + max_single[v2] 其中v1和v2是不同孩子 - 候选3:子树中已有的最大路径,即 max( max_path[v] for v in children ) - 因此,max_path[u] = max( weight[u] + max(0, max_single[v1]) , # 单一路径,考虑可能只取u weight[u] + max_single[v1] + max_single[v2] if exist two children, # 转折路径 max_path[v] for v in children # 子树中的最大路径 ) - 但为了简化,在计算max_single[u]时,max_single[u] = weight[u] + max( {0} ∪ { max_single[v] for v in children } ),因为如果所有子树路径和负,可以只取u。 - 然后max_path[u] = max( max_single[u], # 以u为端点的路径 weight[u] + sum of two largest max_single[v] for v in children, # 转折路径,如果有两个孩子 max( max_path[v] for v in children ) # 子树中的最大路径 ) - 如果只有一个孩子,转折路径项无效。 4. 最后,全局最大路径和是 max_path[root],但root可能不是路径的一部分,所以我们需要在DFS中维护全局最大值。 在实中,最好在DFS过程中更新一个全局变量 max_sum。 算法伪代码: - 初始化 global_max = -infinity - DFS(u): - 如果 u 是叶子节点: - max_single = weight[u] - candidate_max = weight[u] # 局部最大路径,这里就是单个节点 - 更新 global_max = max(global_max, candidate_max) - 返回 max_single - 否则: - 对于每个子节点 v,递归计算 max_single_v = DFS(v) - 计算所有子节点的 max_single 值 - 设 child_max_single 为所有 max_single_v 的列表 - 排序 child_max_single 降序,或找最大两个值 - max_single[u] = weight[u] + max( child_max_single ) # 如果 child_max_single 非空,否则 weight[u] - 在,以 u 为顶点的可能路径: - 单一路径:max_single[u] - 转折路径:如果有至少两个孩子,weight[u] + 两个最大的 child_max_single 值之和 - candidate_max = max_single[u] # 至少是单一路径 - 如果子节点数 >= 2: - 设 top_two = child_max_single 中最大的两个值 - candidate_max = max(candidate_max, weight[u] + top_two[0] + top_two[1]) - 此外,子树中的最大路径已经由 global_max 在递归中更新,但我们需要考虑以 u 为顶点的路径 - 更新 global_max = max(global_max, candidate_max, 以及递归调用中的最大值,但递归调用已经更新了 global_max 对于子树) - 在 DFS 中,当我们返回时,我们更新当前节点的 candidate_max,但子树的最大路径在递归调用中已经更新了 global_max。 - 所以,在 DFS(u) 中,我们计算以 u 为顶点的路径,并更新 global_max,同时子树路径在递归调用中被处理。 标准做法是: - 在 DFS 中,对于每个节点,计算 max_single[u](从 u 向下到叶子的最大路径和) - 同时,计算以 u 为顶点的最大路径和,并更新全局最大值。 - 返回 max_single[u] 用于父节点。 伪代码: function dfs(u): max_single = weight[u] // 从u开始,只包含u local_max = weight[u] // 以u为根的子树的局部最大路径和,初始为只含u 对于每个子节点 v of u: child_single = dfs(v) // 从v向下到叶子的最大路径和 // 但我们需要所有子节点的 child_single 来计算 // 收集所有子节点的 child_single child_singles = [] for each child v: child_singles.append(dfs(v)) if child_singles 不为空: // 计算 max_single[u]:u + 最大子路径 max_child_single = max(child_singles) max_single[u] = weight[u] + max_child_single // 对于 local_max:可能包含转折路径 // 对 child_singles 排序,取最大的两个 对 child_singles 降序排序 top_two = child_singles[0] 和 child_singles[1] 如果存在 // 单一路径:max_single[u] 已经包含 path_through_u = weight[u] if len(child_singles) >= 1: path_through_u += child_singles[0] // 单一路径 if len(child_singles) >= 2: path_through_u += child_singles[1] // 转折路径,但只加一次权重[u] // 权重[u] 只加一次,因为路径经过 u // 转折路径:u + 左边路径 + 右边路径,所以是 weight[u] + child_singles[0] + child_singles[1] // 所以 local_candidate = max( max_single[u], weight[u] + sum of top two ) // 但 max_single[u] 是 weight[u] + max_child,对于单一路径 // 对于转折路径,如果至少有两个孩子,则是 weight[u] + top_two[0] + top_two[1] // 此外,子树中的最大路径可能出在子节点中,但我们在递归调用中更新了 global_max // 所以对于 local_max at u, 我们只关心以 u 为顶点的路径 local_max = max_single[u] // 单一路径 if len(child_singles) >= 2: local_max = max(local_max, weight[u] + child_singles[0] + child_singles[1]) // 但 local_max 可能比子树中的小,但子树的最大值已经通过递归调用考虑了 else: // 没有子节点 max_single[u] = weight[u] local_max = weight[u] // 更新全局最大值 global_max = max(global_max, local_max) // 返回 max_single[u] 给父节点 return max_single[u] 在递归调用中,global_max 在每次访节点时更新。 在开始时,设置 global_max = -infinity。 在叶子节点,local_max 就是 weight[u],所以更新 global_max。 在内部节点,我们计算 local_max 作为以 u 为顶点的最大路径(单边或转折),并更新 global_max。 子树中的最大路径在递归调用中被捕获,因为当我们访子节点时,我们更新了 global_max。 例如,如果最大路径完全在某个子树中,那么当 DFS 到该子树的节点时,它会被更新。 所以这个方法是正确的。 对于一般树,有多个子节点,我们取两个最大的 child_singles 来获得转折路径。 权重:在路径总和中,我们只对节点求和一次,所以对于转折路径,是 weight[u] + 左路径 + 右路径,其中左路径和右路径是来自不同子节点的 max_single。 max_single[v] 包括 v 的权重,所以当我们添加时,不会重复计算。 在 max_single[u] = weight[u] + max_child_single 中,max_child_single 是从子节点开始的最大路径,其中包含该子节点的权重。 在转折路径中,weight[u] + child_singles[0] + child_singles[1],child_singles[0] 包括第一个子节点的权重,等等,所以节点 u 被计算一次,子节点在它们各自的路径中被计算。 路径不包含重复节点。 在,对于用户:我需要用中文回答,结构清晰。 首先,解释题:在树中,每个节点有一个权重,找到一条路径,使得路径上节点权重之和最大。 然后,描述算法:使用深度优先搜索(DFS)和动态规划。 提供伪代码或步骤。 最后,生成相关题。 引用:用户提供了引用,但可能不直接相关。引用[1]是关于二叉树的,类似。引用[2]是关于图中的最长路径,但树中没有环,所以更简单。 在回答中,如果适用,可以提及引用,但这里可能不需要。 系统指令:在末尾添加引用标识,但用户没有提供具体引用,所以可能不需要,或者使用给定的引用。 用户说:“上述引用仅供参考”,所以我可以忽略或简要提及。 在回答中,自然地添加引用标识。 例如,在提到二叉树时,可以说如引用[1]所述。 但用户的题是关于一般树的,我认为。 用户说:“计算树中最长路径的节点权重之和”,所以是树,不一定是二叉树。 算法适用于一般树。 伪代码:由于用户可能想要实,提供清晰的描述。 最终答案结构: 1. 题解释 2. 算法描述 3. 伪代码或步骤 4. 复杂度分析 5. 相关题 使用中文。</think>### 树中最长路径的节点权重之和算法 在树结构(无环连通图)中,每个节点有一个权重(可为正、负或零),目标是找到一条路径(任意两个节点之间的简单路径),使得路径上所有节点权重之和最大。这个题在树中可以通过高效的动态规划算法解决,时间复杂度为 $O(n)$,其中 $n$ 为节点数。由于树的无环特性,最长路径题不会像一般图那样是 NP-难题(如引用[2]所述)[^2]。 #### 算法核心思想 算法的核心是使用**深度优先搜索(DFS)** 结合**动态规划**,遍历树中的每个节点,并维护两个关键值: - **`max_single[u]`**:以节点 $u$ 为起点的向下路径(到叶子节点)的最大权重和(包括 $u$ 自身的权重)。 - 在遍历过程中,计算以 $u$ 为顶点的路径(可能为单一路径或转折路径)的最大权重和,并更新全局最大值。 关键点: - **单一路径**:从 $u$ 向下到叶子节点的路径(例如,$u \to v$)。 - **转折路径**:路径经过 $u$ 并向两个不同子树延伸(例如,$v_1 \to u \to v_2$),这需要至少两个子节点。 - 全局最大权重和会在 DFS 过程中被动态更新。 #### 算法步骤 1. **初始化**: - 设置全局变量 `global_max = -∞`,用于记录最大路径和。 - 选择一个根节点(任意节点,树的结构确保结果不受根选择影响)。 2. **DFS 遍历函数**(递归实): - **输入**:当前节点 $u$。 - **输出**:`max_single[u]`(以 $u$ 为起点的向下路径最大权重和)。 - 过程: - 如果 $u$ 是叶子节点: - `max_single[u] = weight[u]`(权重和仅为自身)。 - 更新 `global_max = max(global_max, weight[u])`。 - 返回 `max_single[u]`。 - 如果 $u$ 有子节点: - 初始化 `max_single[u] = weight[u]`(至少包含自身)。 - 初始化一个列表 `child_singles`,存储所有子节点 $v$ 的 `max_single[v]`(通过递归调用 DFS 计算)。 - 计算 `max_single[u] = weight[u] + max( { max_single[v] for v in children } ∪ \{0\} )`(如果所有子路径和为负,则只取 $u$ 自身)。 - 计算以 $u$ 为顶点的路径最大和 `local_max`: - 单一路径候选:`max_single[u]`。 - 转折路径候选(如果子节点数 ≥ 2):对 `child_singles` 降序排序,取两个最大值,计算 `weight[u] + top_two[0] + top_two[1]`。 - `local_max = max( max_single[u], weight[u] + top_two[0] + top_two[1] )`(如果子节点数 ≥ 2)。 - 更新 `global_max = max(global_max, local_max)`。 - 返回 `max_single[u]`。 3. **执行和结果**: - 从根节点启动 DFS。 - 最终 `global_max` 即为树的最大路径权重和。 #### 伪代码示例 ```python global_max = float('-inf') # 初始化全局最大值 def dfs(u, weight, children): global global_max # 处理叶子节点 if not children[u]: max_single = weight[u] global_max = max(global_max, max_single) return max_single # 递归计算所有子节点的 max_single child_singles = [] for v in children[u]: child_single = dfs(v, weight, children) child_singles.append(child_single) # 计算 max_single[u]:u 自身 + 最大子路径(或 0 如果为负) max_child = max(child_singles) if child_singles else 0 max_single_u = weight[u] + max_child # 计算以 u 为顶点的路径最大和(单边或转折) local_max = max_single_u # 单一路径候选 if len(child_singles) >= 2: sorted_singles = sorted(child_singles, reverse=True) turn_path = weight[u] + sorted_singles[0] + sorted_singles[1] # 转折路径 local_max = max(local_max, turn_path) # 更新全局最大值 global_max = max(global_max, local_max) return max_single_u # 主函数 def max_path_sum(root, weight, children): dfs(root, weight, children) return global_max ``` #### 算法分析 - **时间复杂度**:$O(n)$,每个节点仅访一次,每个节点的处理时间为 $O(\text{degree}(u))$,总时间为 $O(\sum \text{degree}(u)) = O(n)$(树中边数为 $n-1$)。 - **空间复杂度**:$O(n)$,用于递归栈和存储子节点列表。 - **正确性**:基于树的无环特性,DFS 确保所有路径被覆盖;动态规划通过 `max_single` 和转折路径计算,避免重复计算。 - **权重处理**:算法支持正、负或零权重。如果所有权重为负,最大路径和即为最大权重节点自身(如引用[1]中公式的推广)[^1]。 #### 示例说明 假设树结构如下(节点标注为 `节点:权重`): ``` 1:5 / \ 2:10 3:-2 / \ 4:8 5:4 ``` - 最大路径:节点 2 → 1 → 3 → 4,权重和 = 10 + 5 + (-2) + 8 = 21。 - 算法过程: - 节点 4:`max_single=8`, `global_max=8`。 - 节点 5:`max_single=4`, `global_max=8`。 - 节点 3:`child_singles=[8,4]`,`max_single= -2 + max(8,4) = 6`,转折路径 = -2 + 8 + 4 = 10,`local_max=max(6,10)=10`,`global_max=max(8,10)=10`。 - 节点 2:`max_single=10`, `global_max=10`。 - 节点 1:`child_singles=[10,6]`,`max_single=5 + max(10,6)=15`,转折路径 = 5 + 10 + 6 = 21,`local_max=max(15,21)=21`,`global_max=21`。 - 结果:21。 此算法高效且易于实,适用于二叉树或一般树(通过子节点列表处理多叉情况)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值