瓷砖摆放算法

3
我有一个36 x 36英寸的瓷砖区域。

36 x 36

我有8x8、6x6、6x8和4x8等尺寸的方块,可以旋转90度以适应任何可能的位置。我的任务是创建一个应用程序,计算应选择哪些块以及选择多少块,以便所有块都能放入给定的墙壁开口中。在此示例中,开口为36 x 36。
注意:开口应使用尽可能少的瓷砖来填充,表示较大的瓷砖具有优先权。
应该使用哪种算法进行瓷砖放置?
另一个示例。30 x 30的区域如下所示:

30 x 30

50 x 50

50 x 50


你有每种类型的指定块数,还是每种类型都有无限数量? - Bernhard Barker
无限数量的方块。这只是把玻璃方块放入一个墙口。软件应确定最便宜的方法来放置方块,即使用更少的方块更好,但仍要填满整个开口。 - Vlad
我有一个软件可以做到这一点,但我没有访问源代码的权限,因此我必须从头开始重新制作另一个应用程序。 - Vlad
是的,它可能是背包问题。http://zh.wikipedia.org/wiki/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98 - Vlad
这些日子我会很忙。我必须分析一下我到目前为止所做的,所以我需要时间。 - Vlad
我必须与客户确认。很快就会发生。好主意。 - Vlad
3个回答

1

由于amit已经提供了通用案例答案,所以我将提供一个具体的解决方案。对于这四个块大小,假设它是可行的(尺寸为偶数且>=6等),您可以使用半贪心算法:

第一个目标是最大化8x8块的数量。为此,您需要确定每个方向需要多少个6大小的块。对于每个维度,只需检查是否可被8整除。如果不能被整除,则减去6。重复此过程直到可被整除(不应该超过3次)。

无论需要多少次,那就是您在该维度中需要的6x6块的数量。将它们组成一个矩形并放在一个角落里。从8x8块中再组成另一个矩形并将其放在相反的角落里。这两个矩形的角应该相互接触。

现在你可能会有一些剩余的空间,以两个相对角落的矩形的形式存在。我们知道每个矩形的一个维度可被8整除,另一个维度可被6整除。这里的简单方法是使用适当旋转的6x8方块来填充它,但这并不能保证最大数量的大型(8x8)方块。例如,对于50x50,你会剩下两个18x32的矩形。你可以用12个6x8瓷砖来填充它们。你甚至不能比12块更好,但你可以在那里放更多的8x8方块。

如果这不是一个问题,那么你就完成了(万岁)。这种方法的额外好处是你永远不需要使用4x8方块。


如果您确实想要最大化8x8块,那么您需要采取另一步骤。我们在这里集中讨论可被6整除的维度,因为8很容易。每种我们可能需要的大小(8x8、6x8、4x8)都可以完美地堆叠在那里。
对于另一侧,只有3个可能的数字: 6、12和18。如果是其他任何数字,则第一步做错了。然后执行以下操作:
- 对于6,添加一行6x8(无优化) - 对于12,添加一行4x8和一行8x8 - 对于18,添加一行4x8、一行6x8、一行8x8
完成!
为了看到差异,这里有两个50x50的网格:
Blue  - 8x8
Red   - 6x6
Green - 6x8
Gray  - 4x8

basic

这个例子总共有49个方块。蓝色区域是32x32的面积(16个方块),红色区域是18x18的面积(9个方块),其余部分只用6x8的方块填充(24个方块)。

better

这个例子仍然总共有49个,但是有更多的8x8块。这里有24个大块,而不是上一个例子中的16个。现在也使用了4x8块。

你确定最大化8x8块是正确的选择吗?我怀疑在某些情况下,使用较少的8x8块可能会留下一个空间,可以用较少的小块铺砌,而如果使用8x8块,则需要更多的小块。 - templatetypedef
@templatetypedef 起初我持怀疑态度,因此我测试了24到48之间的每个偶数维度。任何其他尺寸应该是类似的,因为24是6和8的最小公倍数。我无法找到更好的解决方案。如果您能想出任何反例,我会很感兴趣。 - Geobits
请注意,第一个方法(示例1)提供的解决方案与pentadecagon的代码示例相同,唯一的区别在于你如何到达那里。对于第二个示例,只有当您添加一行4x8块时才会发生任何更改。然后,您只需将4x8和8x8交换为两个6x8,这不会改变总瓷砖数。 - Geobits

1

这里是Python代码:

def aux(x):
    # in h we store the pre-calculated results for small dimensions
    h = {18:[6,6,6], 16:[8,8], 14:[8,6], 12:[6,6], 10:[6,4], 8:[8], 6:[6], 4:[4]}
    res = []
    while x > 18: 
        # as long as the remaining space is large, we put there tiles of size 8
        res.append(8)
        x -= 8
    if x not in h:
        print("no solution found")
        return []

    return res + h[x]

def tiles( x, y ):
    ax = aux(x) # split the x-dimension into tiles
    ay = aux(y) # split the y-dimension into tiles
    res = [ [ (x,y) for x in ax ] for y in ay ]
    for u in res:
        print(u)
    return res


tiles( 30, 30 )

基本思路是你可以独立解决x和y,然后将这两个解决方案结合起来。
编辑:正如Dukeling所说,这段代码快乐地使用4x6和4x4块,与要求相反。然而,我认为只有在没有其他方法的情况下才会这样做。因此,如果结果包含这些块,则没有其他解决方案。如果您没有Python可用,可以在此处使用此代码:http://ideone.com/HHB7F8,只需在源代码上方按fork即可。

1
你是在假设有一个4x4的块吗?(我的Python已经生疏了)?(通常不建议在非特定语言的问题中发布(非伪代码的)代码而没有太多解释)。 - Bernhard Barker
好的,谢谢。我添加了一些文本。如果有一个偏爱伪代码的政策,我当然会很乐意适应。我只是认为,如果你可以立即运行代码,那就有优势。 - pentadecagon
在回答中加入实际代码确实是有优势的,但您也可以发布伪代码/高级描述或仅链接到代码的现场演示。仅提供实际代码的缺点是对于不熟悉该语言的人来说可能会难以阅读,并且如果问题没有相应的语言标签,那么很可能会有许多这样的人阅读您的回答。 - Bernhard Barker

0

假设您正在寻找一般情况的答案,很抱歉要说 - 但这个问题是NP-Complete。它基本上是Subset Sum Problem的二维变体。

子集和问题:给定一个集合S和一个数字x - 找出是否存在一个子集S,其总和为x

很容易看出,通过将子集和问题减少到大小为1*x的“字段”,并且对于S中的每个s,我们都有一个瓷砖1*s - 一个问题的解决方案也是另一个问题的解决方案。

因此 - 目前没有已知的多项式解决方案,大多数人认为不存在这样的解决方案。
但请注意,还有一种伪多项式动态规划解决方案子集和,在这里也可以使用。


在这种情况下,看起来所寻求的解决方案并不是通用的,但我可能错了。 - utdiscant
你怎么改变那个算法让它能够解决二维问题? - harold
@harold 我还不确定,但我认为通过增加问题的维度,并根据放置瓷砖创建的新(2)个矩形将每个问题分成子问题。还不确定具体如何实现,这就是为什么我只提供了一个指向此解决方案的指针并指出它可能被利用。 - amit
成为NP完全问题并不意味着它是不可行的。这完全取决于问题的规模。现在8x8的块可以被跳过,因为它们被表示为两个相邻的4x8块(有两种方式)。所以,在90度旋转后,我们剩下6x6、6x8、8x6、4x8、8x4。可能并不是那么多... - valdo
@valdo 我同意,我指的是问题的一般情况。显然,解决一个小的NP完全问题(例如8个城市的TSP)很容易。 - amit
考虑到所有块在每个维度上都是小整数倍的2单位,我认为称其为NP完全问题有些牵强。对于一个固定的、有限的尺寸集合,“伪多项式”并不真正是伪的。 - Sneftel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接