问题表明我有一个大矩形,尺寸为 L 和 W,以及无限数量的相同小矩形,尺寸为 l 和 w。L * W 保证大于 l * w。所以我想找出我可以放入大矩形中的最大小矩形数量,而不会重叠。您可以将小矩形放置在您喜欢的任何方向。
1 回答
轴对齐
矩形在平铺时最合适,中间没有空间。所以,如何做到这一点的大纲:
首先,如果您只是将它们平铺,请弄清楚有多少合适:
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
- 找到图块左边缘的最低点。调用这个
y2
y2 = sin((PI * 0.5) - radians) * tileHeight
- 每个添加的图块都将占用
y2
垂直空间,因此适合的数字是:(boxHeight - y1) / y2
我创建了一个小的ideone.com 示例,您也可以使用它。代码相当丑陋,但它可以工作。对于您在评论(13x8, 14x1
)中的示例,它显示:
Rotated 26.23397827148437 degrees
y1 = 6.188525444904378
y2 = 0.8969959689614577
numTiles = 2