【最长链】wikioi1814

本文介绍了一种解决二叉树中寻找最长链问题的方法。通过深度优先搜索(DFS)两次遍历的方式找到二叉树中最长链的长度。首先从根节点开始找到离根节点最远的节点,然后从该节点开始再次遍历得到最长链。

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

最长链 

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

输入描述

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

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

输出描述

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

样例输入

5

2 3

4 5

0 6

0 0

0 0

样例输出

4


做完之前那道题于是我找了一个最长链的题做

就是代码 依然很丑大哭

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

struct ty
{
    long t, next;
}edge[200010];
long head[100010];
bool v[100010];
long dist[100010];
long n, m, maxn, dian;
void insertedge(long x, long y, long k)
{
    edge[k].t = y;
    edge[k].next = head[x];
    head[x] = k;
}
void dfs(long x)
{
    long i = head[x];
    for (long i = head[x]; i != 0; i = edge[i].next)
        if (!v[edge[i].t])
        {
            v[edge[i].t] = true;
            dist[edge[i].t] = dist[x] + 1;
            dfs(edge[i].t);
            v[edge[i].t] = false;
        }
}
int main()
{
    freopen("1814.in", "r", stdin);
    scanf("%d", &n);
    m = 0;
    for (long i = 1; i <= n; i++)
    {
        long t1, t2;
        scanf("%d%d", &t1, &t2);
        if (t1 != 0)
        {
            insertedge(i, t1, ++m);
            insertedge(t1, i, ++m);
        }
        if (t2 != 0)
        {
            insertedge(i, t2, ++m);
            insertedge(t2, i, ++m);
        }
    }

    memset(dist, 0, sizeof(dist));
    memset(v, 0, sizeof(v));
    v[1] = true;
    dfs(1);
    maxn = 0, dian = 0;
    for (long i =1; i<=n ;i++)
    {
        if (dist[i] > maxn)
        {
            maxn = dist[i];
            dian = i;
        }
    }
    memset(dist, 0, sizeof(dist));
    memset(v, 0, sizeof(v));
    v[dian] = true;
    dfs(dian);
    maxn = 0, dian = 0;
    for (long i =1; i<=n ;i++)
    {
       // cout << dist[i]<<' ';
        if (dist[i] > maxn)
        {
            maxn = dist[i];
            dian = i;
        }
    }
    cout << maxn <<endl;
    return 0;
}


<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、付费专栏及课程。

余额充值