【问题标题】:Find Minimum Weight of Simple Path in Tree在树中找到简单路径的最小权重
【发布时间】:2015-02-11 17:09:07
【问题描述】:

我遇到了一个问题,我们想设计一种有效的算法来找到一条重量最轻的简单路径。 (具有最小权重的简单路径)。

我认为这不是多项式解决方案,但有朋友说可能是 O(n) 或 O(n^2) 或 O(n lg n) 会是......!

程序员或专家,会帮助我吗?任何伪代码?

编辑:

这个问题的输入是一棵树 T,边上有整数权重。权重可以是负数、零或正数。路径的长度是路径中边的权重之和。如果没有重复的顶点,则路径是简单的。

输出:找到给定树中权重最小的简单路径。

【问题讨论】:

  • 路径是什么?根节点 -> 任何叶节点?
  • 亲爱的@RatulSharker,我们知道,我们想找到重量最轻的简单路径。路径的长度是边权重的总和。
  • 在树中哪个是图?简单路径是路径是不通过同一顶点两次的路径?它是零长度路径,还是由最轻边缘组成的路径?可能有一些附加条件吗?
  • 也许你是对的。
  • 啊,明白了,路径可以由几个负权边组成,甚至一些正边,如果它们允许构建长的负权路径。

标签: algorithm data-structures graph tree shortest-path


【解决方案1】:

这是一个线性解的伪代码:

res = 0 // A path from a node to itself has a weight 0

// Runs depth first search from the v vertex and returns the weight of
// the shortest path that starts in a descendant of v and ends in v
def dfs(v):
    // The shortest path from a descendant of v to v
    minLength = 0
    // The list of all lengths of paths coming from children
    childLengths = []
    for edge in edges(v):
        childLength = dfs(edge.to)
        // Update the result with a vertical path from the child
        res = min(res, childLength + edge.weight) 
        // Update the minLength based on the child's value
        minLength = min(minLength, childLength + edge.weight)
        childLengths.add(childLength + edge.weight)
    if childLengths.size() >= 2:
        // Update the result based on two smallest children values.
        // Finds the the first and the second minimum in a list in a linear time.
        min1 = firstMin(childLengths)
        min2 = secondMin(childLengths)
        res = min(res, min1 + min2)
    return minLength    

dfs(root)
return res

如果树没有根,我们可以选择任何顶点作为根。

【讨论】:

  • 在线测试的任何实现?
  • @AliMovagher 不,我没有现成的实现。但是将这个伪代码转换为具体的编程语言应该很容易。
【解决方案2】:

在一棵树中,每对节点之间只有一个简单的路径 (there's a proof here)。

因此,即使您尝试每对节点寻找其间的路径,您也会有一个 O(n^3) 算法。

一种更好的方法是为每个节点查找一次访问中每个其他节点的成本。这将算法降低到 O(n^2)。

【讨论】:

【解决方案3】:

有一个简单的 O(n) 算法,它基于这样一个事实:在树中总是有一些叶子,如果我们删除一个叶子,剩下的图仍然是树。所以我们可以一个一个地删除叶子,这样处理所有的树(所有的边)。

您需要为每个节点保留找到的最轻路径。

因此,假设您正在处理一些叶子,并且节点 n 是节点叶子所连接的,我们希望确保您密切关注通过连接此 n 和叶子的边缘的任何路径。看图吧

这里的方向仅表示从较早删除的节点到其“父节点”的方向,稍后将删除的节点,可能取决于您正在处理的叶子的顺序n。

如果我们有穿过叶子的最短路径并且 n 它可以将叶子的任何“孩子”与 n 的其他孩子连接起来,或者它可以将叶子的任何“孩子”连接到树的其余部分。

在这两种情况下,保持从任何已处理节点到当前节点的最短路径就足够了。当我们处理叶子时,我们采用为叶子建立的最轻路径并为其添加连接权重,并将其与已经为 n 保存的最短路径进行比较。这样,我们不可避免地会比较 n 的两条最短路径,并将它们联合起来,并且当 n 的所有“孩子”都将被处理时,将保存 n 的最短路径。

所以这里是伪代码。

for each (node in graph)
{
   node.shortest_path = 0; //zero length path from this node to this node
}
leaves = new list(all leaves in graph); //O(n) to find
int minWeight = 0; //any zero length path is minimum weight path currently found

//note that we a going to add new elements in list during the iteration
//and we will need to iterate through all of them including new added
//so in usual programming languages you will need to use for cycle
for each (leaf in leaves)
{
       //we will have last node in tree with no connection
       //because we will delete it on previous iteration
       //and we don't need to process this node
       //for this problem it is last node, 
       //but it may be not last if we have several trees instead of one
       if (no point connected to leaf) continue; 

     n = only one node still connected to leaf       
     connection = connection between n and leaf

     //check if we can make a short path which goes through n 
     //from one already processed "child" node to another
     if (leaf.shortest_path + 
         connection.weight 
         + n.shortest_path < minWeight  )
     {
         minWeight = leaf.shortest_path+connection.weight+n.shortest_path; 
     }
     //and we need to keep lightest path from any processed "child" to n
     if (leaf.shortest_path + connection.weight < n.shortest_path )
     {
          n.shortest_path = leaf.shortest_path + connection.weight;
          if(n.shortest_path < minWeight )
          {
               minWeight = n.shortest_path;
          }
     }

     //delete connection and 
     connection.delete();
     //if n is now leaf add it to leaf list
     if (n is leaf) 
     {
         leaves.Add(n);
     }
}

所以在主循环中,我们只处理了每个节点一次,所以我们有 O(n) 复杂度。 如果您需要非零长度路径但该算法没有找到它,则意味着所有边缘都具有正权重并且最轻的非零长度路径是最轻的边缘。

【讨论】:

  • 如果此代码部分实际上适用于正长度,则尝试使用最小长度边的正值增加所有边的长度。说(-x)是树中的最小长度,然后为所有具有现有长度的边添加长度(x)。如果不存在负长度,则将 (x) 的值设置为零或根本不需要增加。每当找到解决方案时,从被选为结果最小路径的每个边中减去 (x)。
  • 在具有三个节点和两条边的权重为 -4 和 -8 的树中,最轻的路径由两条边组成,权重为 -12,如果我们将每个权重加 6,我们将有两条边的权重为 2和-2,最轻的路径将由1条边组成,权重为-2。我们并不总是通过将所有边增加相同的值来获得等价的。
  • (x) 将是最小的负值,在您的示例中,每个值将添加 8。这导致长度为 0 和 4。省略 0 是因为它们不会增加任何额外成本,路径将是值 4+0 = 4。有两条边,每条边减去 8 将导致 (4-8) + (0-8) = -12。这是最小长度。
  • 我的代码部分适用于任意整数权重树,在只有正边的树中,如果允许没有边的路径,则最小权重路径为 0,或者最轻边的权重。在第二种情况下,树中最轻的路径具有两条边的权重为 4 和 0,最轻的路径的权重为 0,并且仅包含第二条边。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 2011-06-26
相关资源
最近更新 更多