给定一个树,其中节点具有值(可以是负数),找到最大化路径中节点总和的路径。
我尝试使用动态编程技术来获得解决方案,但无法突破。
答案 0 :(得分:0)
我们将在树上执行泛洪填充算法,从每个叶子开始,并在洪水填充期间在我们发现的路径上运行maximum subsequence sum算法。这为每对叶子产生候选最大值;我们选择其中最大的作为我们的真实答案。这个解决方案需要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
我们需要一个泛洪填充算法。这很简单,虽然它是通过“初始摘要”参数化的,并且是通过一个额外加权节点扩展摘要的方法;这是因为我们将使用泛洪填充算法来查找所有叶子并找到候选路径。我们需要注意的一个技巧是确保我们不回溯;为此,我们在nPrevious
中跟踪上一个节点(如果有)。
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)
}
这个函数很好地插入到我们的泛洪填充算法中,以便从特定节点开始寻找候选路径:
findCandidates :: (Num w, Ord w, Ord n) => Tree w n -> n -> [Summary w n]
findCandidates t n = findLeaves t n >>= flood extend emptySummary t
顶级功能只进行泛洪填充并选择最大候选者。 (关系是任意打破的,因为它们在extend
中。)
maximalPath :: (Num w, Ord w, Ord n) => Tree w n -> n -> WeightedPath w n
maximalPath t n = maximum . map endingAnywhere $ findCandidates t n
让我们看看它的运行情况。我们将定义一个非常简单的样本树:它是一个星形树,其中心有节点0
,节点1
到5
各自连接到中心集线器。该中心的权重为-1
,根据它们所在的节点,叶子的权重为1
到5
。
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 :(得分:0)
在树的每个节点上计算:
1)该节点的任何后代到该节点的总和最大的路径。
2)具有最大总和的路径,可以在以该节点为根的子树中找到。
这两个都可以使用在该节点的子节点计算的值来计算。
在情况(1)中,答案是在该节点处开始和停止的0长度路径,或者该节点处的值与任何子节点处的最大(1)值之和。
在情况(2)中,答案是该节点的两个最大(1)值的总和,减去该节点的值,或者来自任何子节点的最大值(2)。
您要查找的值是根目录下的(2)值,您可以添加额外的簿记来计算所涉及的路径。
这里的成本基本上是递归遍历树的成本,当你从深度优先搜索返回时完成大部分工作,所以我认为它是O(n)