Codevs P1814 最长链

Codevs P1814 最长链


题目描述 Description

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


输入输出 Input&Output


输入描述 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


样例输入 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,所以可以采用2遍BFS的方法来求得,首先从任意一个点进行BFS,找出距其最远的一个点,然后再从最远的点进行一次BFS,此时找出的距离即为最远距离,这是建立在正边权的基础上进行的,如果有负边权,则该方法会有错误,正解应为树形DP,通过计算该点向下的深度加上向上的长度来求的最长链的长度。
该过程是先求得向下的深度,因为要知道该点的深度,必须先求的它的子节点的深度,所以可以先逐层深入到叶子节点,先求的下面的节点深度,再返回求得该点向下的深度。向上的长度是指通过其父节点走到长度,不一定必须向根那个方向走,而是可以通过根再向下走。
那么求得向上的长度必须应用到向下的深度,所以必须先进行计算向下的深度,再计算向上的长度,因为计算一个点向上的长度需要用到父亲的长度,所以要自上而下的计算,计算完该值后在深入到下层计算。


代码如下


DP求法

program p1814;
type point=^rec;
     rec=record
      e,v:longint;
      s:point;
     end;
var n,i,j,s,e,ans,temp:longint;
    up:array[1..1000] of longint;
    down:array[1..1000,1..2] of longint;
    vertex:array[1..10000] of point;
function max(a,b:longint):longint;
begin
 if a>b then exit(a);
 exit(b);
end;
procedure insert(s,e:longint);
var p:point;
begin
 new(p);
 p^.e:=e;
 p^.s:=vertex[s];
 vertex[s]:=p;
end;

procedure down_x(i:longint);
var p:point;
begin
 p:=vertex[i];
 while p<>nil do
  begin
   down_x(p^.e);
   if down[p^.e,1]+1>down[i,1]
    then
     begin
      down[i,2]:=down[i,1];
      down[i,1]:=down[p^.e,1]+1;
     end
    else
     begin
      if down[p^.e,1]+1>down[i,2]
       then
        begin
         down[i,2]:=down[p^.e,1]+1;
        end;
     end;
   p:=p^.s;
  end;
end;

procedure up_x(i:longint);
var p:point;
begin
 p:=vertex[i];
 while p<>nil do
  begin
   up[p^.e]:=up[i]+1;
   if down[i,1]=down[p^.e,1]+1
    then temp:=down[i,2]+1
    else temp:=down[i,1]+1;
   if temp>up[p^.e]
    then up[p^.e]:=temp;
   up_x(p^.e);
   p:=p^.s;
  end;
end;

begin
 readln(n);
 for i:=1 to n do
  begin
   readln(s,e);
   if s<>0 then
    insert(i,s);
   if e<>0 then
    insert(i,e);
  end;
 down_x(1);
 up[1]:=0;
 up_x(1);
 ans:=-maxlongint;
 for i:=1 to n do
  begin
   ans:=max(ans,down[i,1]+up[i]);
  end;
 write(ans);
end.

测试结果

运行结果
测试点#chain0.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#chain1.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#chain2.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#chain3.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#chain4.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#chain5.in 结果:AC 内存使用量: 624kB 时间使用量: 0ms
测试点#chain6.in 结果:AC 内存使用量: 1520kB 时间使用量: 12ms
测试点#chain7.in 结果:AC 内存使用量: 4464kB 时间使用量: 23ms
测试点#chain8.in 结果:AC 内存使用量: 4204kB 时间使用量: 42ms
测试点#chain9.in 结果:AC 内存使用量: 5872kB 时间使用量: 52ms



BFS求法

program line;
type point=^rec;
     rec=record
      e,v:longint;
      s:point;
     end;
     rec2=record
      s,v:longint;
     end;
var n,maxn,maxj,i,j,s,e,v:longint;
    queue:array[1..1000000] of rec2;
    visited:array[1..1000000] of boolean;
    vertex:array[1..100000] of point;
function max(a,b:longint):longint;
begin
 if a>b then exit(a);
 exit(b);
end;
procedure insert(s,e,v:longint);
var p:point;
begin
 new(p);
 p^.e:=e;
 p^.v:=v;
 p^.s:=vertex[s];
 vertex[s]:=p;
end;
procedure bfs(st:longint);
var head,rear:longint;
    p:point;
begin
 head:=1;
 rear:=1;
 queue[head].s:=st;
 queue[head].v:=0;
 visited[st]:=true;
 while head<=rear do
  begin
   p:=vertex[queue[head].s];
   while p<>nil do
    begin
     if not visited[p^.e]
      then
       begin
        inc(rear);
        queue[rear].s:=p^.e;
        queue[rear].v:=queue[head].v+p^.v;
        visited[p^.e]:=true;
        if maxn<queue[rear].v
         then
          begin
           maxn:=queue[rear].v;
           maxj:=p^.e;
          end;
       end;
     p:=p^.s;
    end;
   inc(head);
  end;
end;

begin
 readln(n);
 for i:=1 to n do
  begin
   readln(s,e);
   if s<>0 then
    begin
     insert(i,s,1);
     insert(s,i,1);
    end;
   if e<>0 then
    begin
     insert(i,e,1);
     insert(e,i,1);
    end;
   visited[i]:=false;
  end;
 maxn:=-maxlongint;
 bfs(1);
 for i:=1 to n do visited[i]:=false;
 bfs(maxj);
 if maxn<0 then write(0)
           else write(maxn);
end.

测试结果

运行结果
测试点#chain0.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#chain1.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#chain2.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#chain3.in 结果:AC 内存使用量: 128kB 时间使用量: 1ms
测试点#chain4.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms
测试点#chain5.in 结果:AC 内存使用量: 624kB 时间使用量: 0ms
测试点#chain6.in 结果:AC 内存使用量: 1520kB 时间使用量: 12ms
测试点#chain7.in 结果:AC 内存使用量: 4464kB 时间使用量: 23ms
测试点#chain8.in 结果:AC 内存使用量: 4204kB 时间使用量: 42ms
测试点#chain9.in 结果:AC 内存使用量: 5872kB 时间使用量: 52ms


Forever&always

Once upon a time
回到那个夜晚
I believe it was a Tuesday When I caught your eye
我确定那时在星期二的晚上,我们目光相遇
We caught onto something
某些事将要发生在我们身上
I hold onto the night
相信那个晚上
you looked me in the eye and told me you loved me
你看着我,说了你爱我
Were you just kidding?
这不是在骗我吧?
Cause it seems to me,This thing is breaking down
因为这一切现在对我而言早已面目全非了
we almost never speak.I don’t feel welcome anymore
形同陌路的我们让我感觉被抛弃了
Baby what happened, Please tell me
亲爱的,请告诉我,到底发生了什么
Cause one second it was perfect
原本还是那么完美的一切
Now you’re halfway out the door
下一秒你已转身离去
And I stare at the phone, he still hasn’t called
我紧握着电话,但它没有如愿响起
And then you feel so low, you can’t feel nothing at all
这一切让人心灰意冷
And you flashback to when he said, forever and always
你承诺的永远还回响在我的耳边
Oh, and it rains in your bedroom, everything is wrong
噢,我躲在卧室哭泣,一切都变了
It rains when you’re here and it rains when you’re gone.
你离开了,雨却依旧下着
Cause I was there when you said forever and always.
我还停留在你向我承诺时的时光
Was I out of line? Did I say something way too honest
是我做错了什么?还是我说话太过直接?
Did you run and hide like a scared little boy
竟让你像个哭闹的小孩只会躲起来
I looked into your eyes.
看着你的眼睛
Though I knew you for a minute,Now I’m not so sure
原本熟悉的你竟变得如此陌生
So here’s to everything, coming down to nothing
原本美好的一切都不复存在
Here’s to silence that cuts me to the core
你的沉默不语彻底伤害了我
Where is this going?
以后会怎样
Thought I knew for a minute,but I don’t anymore
我真的不知道
And I stare at the phone, he still hasn’t called
我紧攥着电话,它仍然没有响起
And then you feel so low, you can’t feel nothing at all
这真让人心灰意冷
And you flashback to when he said, forever and always
你承诺的永远还回响在我的耳边
Oh, and it rains in your bedroom, everything is wrong
噢,我的天空下起大雨,我的世界不再美好。
It rains when you’re here and it rains when you’re gone.
你离开了,雨却依旧下着
Cause I was there when you said forever and always.
我还停留在你向我承诺时的时光
Did you mean it baby? I don’t think so
现在我终于明白你的承诺只是说说而已
Back up, baby back up
回来吧,宝贝回来吧
Did you forget everything?
你忘记了你做过的一切吗?
Back off, baby back off
后退,宝贝,后退
Did you forget everything?
你忘记了你做过的一切吗?
Cause it rains in your bedroom,, everything is wrong
哦,我的天空下起大雨,我的世界不再美好
It rains when you’re here and it rains when you’re gone
你离开了,雨却依旧下着
Cause I was there when you said forever and always
我还停留在你向我承诺时的时光
And I stare, at the phone
我紧盯着电话
He still hasn’t called
他依然没有打来
And then you feel so low you can’t feel nothing at all
这真让人心灰意冷
And you flashback to when we said
你承诺的永远还回响在我的耳边
Forever and always
永远,永远
And it rains in your bedroom, everything is wrong
我的天空下起大雨,我的世界不再美好。
It rains when you’re here and it rains when you’re gone
你离开了,雨却依旧下着
Cause I was there when you said
当我在这里的时候你说Forever and always
永远,永远
He didn’t mean it baby
他只是说说而已
You said forever and always
你说的永远永远


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

余额充值