问题

给出一个若干个圆,将他们放到矩形框中,与矩形框的下底面相切,圆与圆之间不可以相交,求一个圆的排列方式使得所有圆中最左侧的点到最右侧的点距离水平距离最小

解析

计算两圆左右两侧距离

  • 图片说明 如图所示,对于现切的两个圆,他们两个圆心之间的距离可以通过勾股定理计算,设距离为,而对于如图所示的两圆最远端的水平距离为
  • 如图所示,图片说明 此时两圆的最远距离为
  • 互换同理,计算新加入的圆时需要同时记录最右侧和最左侧的位置
    其中右侧的距离为的圆心位置
  • 图片说明 如图所示,后加入的圆可能不与相邻的圆现切,而是与前面的圆现切

    新加入一个圆后的计算方式

  • 依次计算新加入的圆与已经加入的圆的现切形成的圆心位置,容易得出新圆的圆心位置一定是最远的那个位置
  • 将新得出的圆心位置减去圆的半径,得出最右侧距离,加上圆的半径,得出最左侧距离。分别与现有的最右最左取最小值和最大值

    得出最优排列的方式

  • 在得到上述计算宽度的方法后,只需要对所有可能的排列进行列举并一一计算其宽度,取最小值即可
  • 不难发现这个问题满足多米洛性质,因此可以可以用回溯法,在知晓前个圆排列的情况下,可以求出插入第个圆后的答案
  • 一点点分支限界:如果当前已经排列好的圆宽度超过目前的最后答案,则不必继续尝试,可以直接减枝尝试其他的排列方式

    设计

    minWidth = inf
    Circles = the set of Circle
    Search(numberOfCircle,leftMost,rightMost):
      for i in Circles:
          if not used Circle[i]:
              if it is the first Circle:
                  Center of Circle = Circle.raidus;
              else
                  for j in usedCircles:
                      Center of Circle = max(Center of Circle,calcute the new Center);    
              currentLeftMost = min(leftMost,Center of Circle - Circle.radius);
              currentRightMost = max(rightMost,Center of Circle + Circle.radius);
              if it is the last Circle:
                 if currentRightMost-currentLeftMost<minWidth:
                      minWidth = currentRightMost-currentLeftMost
                      bestPermutation = currentPermutation
                      exit
              else
                  Search(numberOfCircle,currentLeftMost,currentRightMost);

    分析

    在所有的运算中,计算圆圆之间的关系最为复杂,复杂度的衡量以此为标准
    个圆的情况为例,以下是这个圆排列可能出现的所有情况图片说明
    复杂度的上界就是决策树上点的总个数,除第一层一个空节点外,每一层节点的个数都是上一层节点个数的倍,其中是未被选中的点的个数


同时每个节点内要进行次计算来求出实际的圆心位置
最终复杂度

源码

传送门