将二叉树划分为具有相似大小的k个部分

时间:2014-07-29 14:34:08

标签: algorithm tree binary-tree partition np-hard

我试图将二叉树分成k个类似大小的部分(通过删除k-1边缘)。这个问题有没有有效的算法?还是NP难?任何指向论文,问题定义等的指针?

- 评估分区质量的一个合理指标可能是最大和最小分区之间的大小差距;另一个指标可能是使最小的分区具有尽可能多的顶点。

2 个答案:

答案 0 :(得分:3)

这是一个多项式确定性解决方案:

  1. 假设树是有根的,并且有两个固定值:MINMAX - 一个组件的最小和最大允许大小。

  2. 然后可以使用动态编程来检查是否存在分区,使得每个组件大小介于MINMAX之间:

  3. 我们假设f(node, cuts_count, current_count)true当且仅当有{...}}方法在cuts_count的子树中进行精确node次切割时才current_count顶点连接到node,因此条件2)成立。

  4. 当且仅当f(leaf, 1, 0)和{{1}时,叶子的基本情况为:true(从父项到叶子的边缘切割)为MIN <= 1 } MAX >= 1(不要剪切)总是f(leaf, 0, 1)truefalse的所有其他值都是cuts_count

  5. 要为current_count(不是叶子)计算f,可以使用以下算法:

    node

    这个伪代码的作用是组合节点子节点的所有可能状态来计算此节点的所有可达状态(第一组for循环),然后通过切断此节点与其父节点之间的边缘来生成其余可达状态(第二组for循环)(状态表示//Combine all possible children states. for cuts_left in 0..k for cuts_right in 0..k for cnt_left in 0..left_subtree_size for cnt_right in 0..right_subtree_size if f(left_child, cuts_left, cnt_left) is true and f(right_child, cuts_right, cnt_right) is true and then f(node, cuts_left + cuts_right, cnt_left + cnt_right + 1) = true //Cut an edge from this node to its parent. for cuts in 0..k-1 for cnt in 0..node's_subtree_size if f(node, cuts, node's_subtree_size) is true and MIN <= cnt <= MAX: f(node, cuts + 1, 0) = true 元组。如果(node, cuts_count, current_count)f(state),我称之为可到达。
    具有两个子节点的节点就是这种情况,具有一个子节点的情况可以是类似方式的处理。

  6. 最后,如果truef(root, k, 0),则可以找到对条件2)进行分层的分区,否则就不可能。我们需要“假装”我们在这里进行了true切割,因为当我们为k计算f时,我们还从根到它的父(这个边缘并且这个父实际上不存在)中切出了一个虚边。 root(以避免角落案例)。

  7. 此算法的空间复杂度(针对固定MINMAX)为O(n^2 * k)n是节点数),时间复杂度为{ {1}}。似乎复杂性实际上是O(k^2 * n^2),但并非如此,因为节点左右子树中顶点数的乘积恰好是节点对的数量,因此它们的最小公共祖先就是这个节点。但是节点对的总数是O(k^2 * n^3)(并且每对节点只有一个最不共同的祖先)。因此,所有节点上左右子树大小的乘积之和为O(n^2)

  8. 您可以简单地尝试所有可能的O(n^2)MIN值并选择最佳值,但可以更快地完成。关键的观察是,如果有MAXMIN的解决方案,则始终存在MAXMIN的解决方案。因此,可以迭代MAX + 1MIN个不同值的所有可能值)并应用二进制搜索来查找给出有效解(n / k次迭代)的最小MAX 。因此总体时间复杂度为log n。但是,如果您想最大化O(n^2 * k^2 * n / k * log n) = O(n^3 * k * log n)(不是为了最小化MINMAX之间的差异),您只需使用此算法并忽略MIN值(通过设置)它的值为MAX)。然后,不需要对n进行二进制搜索,但可以通过MAX进行二进制搜索,并获得MIN解决方案。

  9. 要重建分区本身,可以从O(n^2 * k^2 * log n)开始并应用我们用于计算f(root, k, 0)的步骤,但这次是相反的方向(从根到叶)。还可以保存有关如何获取每个状态的值的信息(组合子状态或切割边缘之前的状态)(并在f的初始计算期间适当更新它)然后使用这些数据重建分区(如果我对此步骤的解释看起来不是很清楚,阅读有关动态编程和重建答案的文章可能有帮助。)

  10. 因此,在二叉树上存在针对此问题的多项式解决方案(即使对于任意图形它是NP难的)。

答案 1 :(得分:3)

我可以建议非常快速的解决方案,使最小的部分具有尽可能多的顶点度量。

假设我们猜测最小部分的大小S,并想检查它是否正确。 首先,我想发表一些声明:

  1. 如果树的总大小大于S,则至少有一个小于S的子树,并且该子树的所有子树都较小。 (这足以检查两者。)

  2. 如果有一些方法来分割最小部分的大小&gt; = S的树,并且我们有子树T所有小于S的子树,我们可以授予T内没有边缘被删除。 (因为任何此类删除都会创建一个小于S的分区)

  3. 如果有一些方法来分割树的最小部分的尺寸&gt; = S,并且我们有一些尺寸&gt; = S的子树T,内部没有删除边但是不是部分之一,我们可以用其他方式分割树,其中子树T将是其中一个部分本身,并且所有部分都不小于S.(只需将一些额外的顶点从原始部分移动到任何其他部分,这个其他部分不会变小。)< / p>

  4. 所以这里有一个算法来检查我们是否可以将树分割成不小于S的k部分。

    1. 找到所有合适的顶点(大小的子树的根>&=; S和两个子的大小&lt; S)并将它们添加到列表中。当子树大于S时,您可以从根开始并遍历所有顶点。

    2. 虽然列表不为空且零件数量较少,但K从列表中取一个顶点并将其从树上剪下。比更新父顶点的子树大小,如果其中一个变为合适,则添加到列表中。 您甚至不需要更新所有父顶点,直到您首先找到新的子树大小大于S,父顶点不适合在列表中添加,并且可以在以后更新。

      < / LI>
    3. 您可能需要构建树以恢复分配给顶点的原始子树大小。

    4. 现在我们可以使用二分法。我们可以确定上限为Smax = n / k,并且可以从等式(2 * Smin-1)*(K-1)+ Smin = N中检索下限,如果我们将切断k-1子树,它将授予每个大小为Smin - 1的两个子子树,我们将留下一小部分Smin。 Smin =(n + k -1)/(2 * k - 1) 现在我们可以检查S =(Smax + Smin)/ 2 如果我们设法使用上面的方法构造分区,而不是S小于或等于它的最大可能值,那么构造分区中的最小部分也可能比S大,我们可以设置新的下限而不是S,如果我们失败S比可能的大。

      一次检查的时间复杂度是k乘以每次更新的父节点的数量,因为更新节点的平衡树数量是恒定的(我们将使用之前解释的技巧并且不会更新所有父节点),仍然不是更大在最坏的情况下,最终不平衡的树比(n / k)。搜索合适的顶点具有非常相似的行为(搜索时传递的所有顶点将在稍后更新。)。

      n / k与(n + k -1)/(2 * k - 1)之间的差值与n / k成正比。

      因此,如果我们有预先计算的子树大小,我们有时间复杂度O(k * log(n / k)),如果没有预先计算子树大小,则为O(n),O(n * log(n / k) )在最坏的情况下。

      这种方法可能导致最后一部分相对较大的情况,但我想一旦你有了建议的方法,你可以找出一些改进来减少它。