Algorithm 带加权节点的树行走

Algorithm 带加权节点的树行走,algorithm,tree,Algorithm,Tree,给定一棵树,其中节点有一个值(可以是负值),找到使路径中的节点之和最大化的路径 我尝试使用动态规划技术获得解决方案,但无法突破。我们将在树上执行洪水填充算法,从每片叶子开始,在洪水填充期间发现的路径上运行该算法。这为每对叶子产生一个候选最大值;我们选择其中最大的一个作为我们的真实答案。这个解决方案需要O(n^2)个时间:有O(n)个叶子,我们在每个叶子上做O(n)个工作来完成最大子序列和问题的泛洪填充版本,产生候选生产阶段的O(n^2)个工作总数。然后我们做O(n^2)工作,从生成的O(n^2)

给定一棵树,其中节点有一个值(可以是负值),找到使路径中的节点之和最大化的路径


我尝试使用动态规划技术获得解决方案,但无法突破。

我们将在树上执行洪水填充算法,从每片叶子开始,在洪水填充期间发现的路径上运行该算法。这为每对叶子产生一个候选最大值;我们选择其中最大的一个作为我们的真实答案。这个解决方案需要O(n^2)个时间:有O(n)个叶子,我们在每个叶子上做O(n)个工作来完成最大子序列和问题的泛洪填充版本,产生候选生产阶段的O(n^2)个工作总数。然后我们做O(n^2)工作,从生成的O(n^2)个候选中选择最大路径

我将给出Haskell中的一个示例实现。有些导入内容通常可以忽略:

import Data.List
import Data.Monoid
为简单起见,我们将树表示为函数,给定一个节点,该函数告诉权重和可用的邻居。命名类型和术语时,我将使用
w
表示权重,使用
n
表示节点

type Tree w n = n -> (w, [n])
有一种方法可以方便地引用路径及其权重:

type WeightedPath w n = (Sum w, [n])
在我们的行走过程中,我们将保留两个统计信息,即,在当前节点结束的最大路径,以及我们迄今为止看到的最大路径。我们还将为对应于空树的“空摘要”设置一个常量:无路径和零权重

data Summary w n = Summary
    { endingHere     :: WeightedPath w n
    , endingAnywhere :: WeightedPath w n
    }

emptySummary :: Num w => Summary w n
emptySummary = Summary mempty mempty
我们需要一个洪水填充算法。它非常简单,尽管它由“初始摘要”参数化,并通过一个额外的加权节点扩展摘要;这是因为我们将使用泛洪填充算法来查找所有树叶和候选路径。我们需要注意的一个技巧是确保我们不会倒退;为此,我们在前一个中跟踪前一个节点(如果有)

flood :: Eq n => (w -> n -> s -> s) -> s -> Tree w n -> n -> [s]
flood extend summary t = go Nothing summary where
    go nPrevious summary nCurrent = case maybe id delete nPrevious ns of
        [] -> [summary']
        ns -> ns >>= go (Just nCurrent) summary'
        where
        (w, ns)  = t nCurrent
        summary' = extend w nCurrent summary
例如,我们可以通过将“摘要”设置为我们看到的最后一个节点来查找所有叶子。如果树是空的,我们会厌恶地举起双手:

findLeaves :: Eq n => Tree w n -> n -> [n]
findLeaves = flood (\w n s -> n) undefined
我们可以通过一个节点扩展路径候选摘要,如链接算法中所示:

extend :: (Num w, Ord w, Ord n) => w -> n -> Summary w n -> Summary w n
extend w n s = answer where
    answer = s
        { endingHere = max (0, []) ((Sum w, [n]) <> endingHere s)
        , endingAnywhere = max (endingHere answer) (endingAnywhere s)
        }
顶级函数只是执行泛洪填充并选择最大候选对象。(领带任意断裂,就像在
extend
中一样)

让我们看看它运行。我们将定义一个非常简单的示例树:它是一个星形树,节点
0
位于其中心,节点
1
5
分别连接到中心集线器。中心具有权重
-1
,叶片根据其所在节点通过
5
进行权重
1

sampleTree :: Tree Int Int
sampleTree 0 = (-1, [1..5])
sampleTree n = (n, [0])
在ghci中,我们现在可以通过提供树和树中的任何节点来找到最大路径:

> maximalPath sampleTree 0
(Sum {getSum = 8},[5,0,4])

…表示最大和为
8
,可通过从外部节点5到中心节点0,然后向外到节点4的路径来实现。

在树的每个节点计算:

1) 从该节点的任何子代到该节点的总和最大的路径

2) 在以该节点为根的子树中可以找到的最大和的路径

这两个都可以使用在该节点的子节点处计算的值来计算

在案例(1)中,答案要么是从该节点开始并停止的0长度路径,要么是该节点上的值与任何子节点上的最大(1)值之和

在案例(2)中,答案要么是该节点的两个最大(1)值之和减去该节点的值,要么是任何子节点的最大(2)值

您要查找的值是根目录下的(2)值,您可以添加额外的簿记来计算所涉及的路径


这里的成本基本上是递归遍历树的成本,在深度优先搜索返回时完成大部分工作,因此我认为这是O(n)

为了在该站点上获得帮助,您需要发布一些代码。请详细说明您的答案。你能再举几个例子吗p@A.S.H我很乐意。你想看什么样的例子?这个算法能处理圣诞树吗?(只是开玩笑)我相信确实有一个更便宜的动态规划解决方案,我已经添加了一个答案。
sampleTree :: Tree Int Int
sampleTree 0 = (-1, [1..5])
sampleTree n = (n, [0])
> maximalPath sampleTree 0
(Sum {getSum = 8},[5,0,4])