覆盖具有相同小矩形的大矩形

时间:2013-08-30 16:02:41

标签: algorithm geometry rectangles

问题表明我有一个大矩形,尺寸为L和W,以及无限数量的相同小矩形,尺寸为l和w。 L * W保证大于l * w。所以我想找出可以放入大矩形的小矩形的最大数量,而不会重叠。您可以将小矩形放在您喜欢的任何方向。

1 个答案:

答案 0 :(得分:1)

Axis Aligned

矩形在平铺时最适合,两者之间没有空隙。所以,概述了如何做到这一点:

首先,如果你只是将它们平铺在一起,弄清楚有多少适合:

lFit = L / l // integer division for these, no remainder
wFit = W / w

你知道如果你把它们放在与大矩形相同的方向上,你可以在里面放置lFit * wFit个矩形。再次检查其他方向,并选择较大的方向作为基础。

现在你可能还剩下一些空间。那个空间由矩形组成。您可以从上一步轻松找到这些大小。再次为这些较小的矩形运行它并添加到基数。递归直到不再适合。

如果没有任何瓷砖适合“剩余的”较小的矩形,则需要检查倾斜的矩形。

对角线

一旦您的瓷砖不适合轴对齐到矩形中,您需要倾斜它。您可以通过倾斜它们 来包装最多的瓷砖,以适应最长的盒子尺寸,并将其牢固地放在三个墙壁上。然后你尝试在它下面堆叠更多。

注意:对于这里的所有数学,我使用width作为“最长边”。如果你的矩形/瓷砖不匹配,只需翻转尺寸。

要确定正确的旋转角度,您可以使用试验/错误二进制搜索。我确信有一种更“肮脏”的方式来做,但这种方法效果很好。旋转矩形的边界宽度的公式是(以弧度表示的角度):

width = w * cos(angle) + h * sin(angle)

要进行试验/错误,只需将其循环直至达到容差:

// boxWidth, tileWidth, tileHeight
public static double getAngle(double bw, double tw, double th){
    double err = 10;
    double maxAngle = PI * 0.25; // 45 degrees, any more would be taller than wide
    double angle = maxAngle * 0.5; // start in the middle
    double angleDelta = angle * 0.5; // amount to change;
    count = 0;
    while(count++ < 100){
        double rotatedWidth = tw * Math.cos(angle) + th * Math.sin(angle);
        err = rotatedWidth - bw;
        if(Math.abs(err) < TOLERANCE){
            return angle;
        } else if(err < 0){
            angle -= angleDelta;
        } else {
            angle += angleDelta;
        }
        angleDelta *= 0.5;
    }
    return -1; // found no good angle in 100 attempts
}

一旦你有角度,你可以使用基本触发来找出其他一些点:

  • 找到要放置的图块顶部边缘的最低y点。致电此y1
    • y1 = sin(angle) * tileWidth
  • 找到图块的 left 边缘的最低点。致电此y2
    • y2 = sin((PI * 0.5) - radians) * tileHeight
  • 每个添加的图块将占用y2个垂直空间,因此适合的数字为:
    • (boxHeight - y1) / y2

我创建了一个你可以玩的小ideone.com example。代码相当丑陋,但它的工作原理。对于您在评论中的示例(13x8, 14x1),它会显示:

Rotated 26.23397827148437 degrees
y1 = 6.188525444904378
y2 = 0.8969959689614577
numTiles = 2