我计算出一种我认为在O(n * k)运行时运行的算法。下面是伪代码:
routine heaviestKPath( T, k )
// create 2D matrix with n rows and k columns with each element = -∞
// we make it size k+1 because the 0th column must be all 0s for a later
// function to work properly and simplicity in our algorithm
matrix = new array[ T.getVertexCount() ][ k + 1 ] (-∞);
// set all elements in the first column of this matrix = 0
matrix[ n ][ 0 ] = 0;
// fill our matrix by traversing the tree
traverseToFillMatrix( T.root, k );
// consider a path that would arc over a node
globalMaxWeight = -∞;
findArcs( T.root, k );
return globalMaxWeight
end routine
// node = the current node; k = the path length; node.lc = node’s left child;
// node.rc = node’s right child; node.idx = node’s index (row) in the matrix;
// node.lc.wt/node.rc.wt = weight of the edge to left/right child;
routine traverseToFillMatrix( node, k )
if (node == null) return;
traverseToFillMatrix(node.lc, k ); // recurse left
traverseToFillMatrix(node.rc, k ); // recurse right
// in the case that a left/right child doesn’t exist, or both,
// let’s assume the code is smart enough to handle these cases
matrix[ node.idx ][ 1 ] = max( node.lc.wt, node.rc.wt );
for i = 2 to k {
// max returns the heavier of the 2 paths
matrix[node.idx][i] = max( matrix[node.lc.idx][i-1] + node.lc.wt,
matrix[node.rc.idx][i-1] + node.rc.wt);
}
end routine
// node = the current node, k = the path length
routine findArcs( node, k )
if (node == null) return;
nodeMax = matrix[node.idx][k];
longPath = path[node.idx][k];
i = 1;
j = k-1;
while ( i+j == k AND i < k ) {
left = node.lc.wt + matrix[node.lc.idx][i-1];
right = node.rc.wt + matrix[node.rc.idx][j-1];
if ( left + right > nodeMax ) {
nodeMax = left + right;
}
i++; j--;
}
// if this node’s max weight is larger than the global max weight, update
if ( globalMaxWeight < nodeMax ) {
globalMaxWeight = nodeMax;
}
findArcs( node.lc, k ); // recurse left
findArcs( node.rc, k ); // recurse right
end routine
让我知道你的想法。欢迎提供反馈。
我认为已经提出了两种天真的算法,它们在加权二进制树中找到最重的长度约束路径。首先,该算法的描述如下:给定一个带有加权边和一些值k的n-顶点二叉树,找到长度为k的最重路径。
对于这两种算法,我需要对所有顶点的引用,所以我只需要对树进行简单的遍历,以便对所有顶点进行引用,每个顶点都有对其左,右和父的引用树中的节点。
算法1 对于这个算法,我基本上计划从树中的每个节点运行DFS,同时考虑固定的路径长度。另外,由于我正在寻找的路径有可能从左子树到根到右子树,我将不得不考虑每个节点的3个选择。但这会导致O(n * 3 ^ k)算法,我不喜欢这样。
算法2 我基本上考虑使用Dijkstra's Algorithm的修改版本来考虑固定的路径长度。由于我正在寻找最重的并且Dijkstra的算法找到最轻的,我打算在开始遍历之前否定所有边缘权重。实际上......这没有意义,因为我必须在每个节点上运行Dijkstra,并且这似乎不比上述算法更有效。
所以我想我的主要问题有几个。首先,我上面描述的算法能解决手头的问题吗?我并不完全确定Dijkstra的版本会起作用,因为Dijkstra的版本是为了获得正边缘值。
现在,我确信存在更多聪明/有效的算法...什么是更好的算法?我读过“使用脊柱分解来有效地解决树长度受限的最重路径问题”,但这真的很复杂,我根本不理解它。是否有其他算法可以解决这个问题,可能不像脊柱分解那样有效但更容易理解?
答案 0 :(得分:3)
您可以在 k 边缘之后停止的每个节点向下使用DFS来搜索路径,但请注意,这将在每个节点上执行2 ^ k工作,总共为O(n * 2 ^ k)工作,因为从起始节点开始,每个级别的路径数量都会翻倍。
正如DasBoot在评论中所说的那样,在这里使用Dijkstra算法没有任何优势,因为它的聪明程度相当于选择最短(或最长)的方式来获得多个路线可能的两个点之间。有了树,总有一条路。
我有一个动态编程算法需要O( nk )时间。以下是一些提示:
这应该足以让你思考正确的方向;如果您需要进一步的帮助,请告诉我。
答案 1 :(得分:2)
这是我的解决方案。欢迎提供反馈。
让我们将二叉树视为有向图,边缘从父级到子级。让我们为每个顶点v定义两个概念:
a)弧:它是一个有向路径,也就是说,它从顶点v开始,路径中的所有顶点都是起始顶点v的子节点。
b)一个子路径:这是一个包含v的定向或非定向路径,也就是说,它可以从任何地方开始,到任何地方结束,从v的孩子到v,然后,对其他孩子说。弧集是子路径集的子集。我们还定义了一个函数HeaviestArc(v,j),它为顶点j给出了最长的弧,在左侧或右侧,长度为j,从v开始。我们还定义了LeftHeaviest(v,j) )和RightHeaviest(v,j)分别是长度为j的最重的左右弧。
鉴于此,我们可以根据子节点v为每个顶点v定义以下重复:
LeftHeaviest(v,j) = weight(LeftEdge(v)) + HeaviestArc(LeftChild(v)),j-1);
RightHeaviest(v,j) = weight(RightEdge(v)) + HeaviestArc(RightChild(v)),j-1);
HeaviestArc(v,j) = max(LeftHeaviest(v,j),RightHeaviest(v,j));
这里j从1到k,HeaviestArc(v,0)= LeftHeaviest(v,0),RightHeaviest(v,0)= 0表示全部。对于叶节点,HeaviestArc(v,0)= 0,而HeaviestArc(v,j)= - inf用于所有其他j(我需要更彻底地考虑角点情况)。
然后HeaviestChildPath(v),包含v的最重的子路径可以计算为:
HeaviestChildPath(v) = max{ for j = 0 to k LeftHeaviest(j) + RightHeaviest(k-j)}
最重的路径应该是所有儿童路径中最重的路径。
算法的估计运行时间应为O(kn)。
答案 2 :(得分:0)
def traverse(node, running_weight, level):
if level == 0:
if max_weight < running_weight:
max_weight = running_weight
return
traverse(node->left,running_weight+node.weight,level-1)
traverse(node->right,running_weight+node.weight,level-1)
traverse(node->parent,running_weight+node.weight,level-1)
max_weight = 0
for node in tree:
traverse(node,0,N)