使用精确覆盖问题的Algorithm X算法解决Pentomino问题

4
我已经工作了几天,最终决定在这里发布我的问题。我将详细解释我正在做什么以及如何进行。 在继续之前,我查阅了维基百科和其他20个解释此问题的网站,但这并没有帮助我。
链接1:Dancing Links - 维基百科 链接2:Knuth的算法x。 其中一个更有用的网站:链接3:Kanoodle,但是当涉及到我的问题时,解释非常差。
问题:如何用W、I和L形五格拼图覆盖5xn的矩形?您可以翻转和旋转它们。你可能会问什么是五格拼图?五格拼图由五个方块组成,共同构建一个图形。例如W形五格拼图。
   | A | B | C
---------------
a  | 1 | 0 | 0 |
-------------
b  | 1 | 1 | 0 |    Imagine all the 1s together they build a big "W".
-------------       If you look at my picture it will be clearer.
c  | 0 | 1 | 1 |

不是在5xn的场地上实现W、L和I型五格方块,我简化了任务,开始思考在5x3的场地中填充"W"的所有可能方法。 就像这样。 enter image description here 我的下一步是思考一个矩阵,它看起来类似于Knuth的DLX算法,我得到了这个。黄色和橙色之间的绿线意味着您可以将两者放在一起以获得另一个有效的解决方案。我的下一个文本描述了这条绿线。 enter image description here 我注意到,如果我取一行并检查该行下面或上面的任何其他行是否在同一列中没有1,则我有一个有效的解决方案。但我不知道如何实现它。经过数小时的研究,我找到了另一种描述我的问题的方法。我取了一个子集(我的W型五格方块)并将其定义为这样。 ---picture. 再次遇到了实现难题。

所以这是我的问题:你会如何在JAVA中实现这个问题?我的方法好吗?你能接受我的想法吗?如果可以,你会怎么做?如果不行,你能告诉我我的思路出了哪些问题吗?

这是给我的代码。

package data;

public class PentominoWILDLX
{
    static Node h;          // header element

    static int cnt;         // count solutions

    public PentominoWILDLX()
    {
        h = new Node();     // init header element
        cnt = 0;            // init counter
    }


    public static void search (int k)           // finds & counts solutions
    {
        if(h.R == h)                            // if empty: count & done
        {
            cnt++; return;                      // choose next column c
        }
        Node c = h.R;                           // remove c from clumns
        cover(c);                               // choose next colun c
        for (Node r=c.D; r!=c; r=r.D)           // forall rows with 1 in c
        {
            for(Node j=r.R; j!=r; j=j.R)        // forall 1-elements in row
            {
                cover(j.C);                     // remove clumn
            }
            search(k+1);                        // recursion with k+1 << hier überpfüen ob ich ändern muss
            for (Node j=r.L; j!=r; j=j.L)       // forall 1-elemnts in row
            {
                uncover(j.C);                   // backtrack . unremove? << googlen
            }
            uncover(c);                         // unremove f c to coulmns
        }
    }



    public static void cover (Node c)           // remove clumn c
    {
        c.R.L = c.L;                            // remove header
        c.L.R = c.R;                            // from row list
        for (Node i=c.D; i!=c; i=i.D)           // forall rows with 1
        {
            for (Node j=i.R; i!=j; j=j.R)       // forall elem in row
            {
                j.D.U = j.U;                    // rmove row elemnt
                j.U.D = j.D;                    // from column list
            }
        }
    }

    public static void uncover (Node c)         // undo remove col c
    {
        for (Node i=c.U; i!=c; i=i.U)           // forall rows with 1
        {
            for (Node j=i.L; i!=j; j=j.L)       // for all elem in row
            {
                j.D.U = j;                      // unremove row ele,
                j.U.D = j;                      // to lumn list
            }
            c.R.L = c;                          // unremove header
            c.L.R = c;                          // to row list
        }
    } // end of class

    class Node                  // represents 1 element or header
    {
        Node C;                 // reference to column-header << h?,
        Node L;                 // left
        Node R;                 // right
        Node U;                 // reference up
        Node D;                 // down reference down

        Node()
        {
            C=L=R=U=D=this;     // 2*double-linked circular list
        }
    }   // end of class

    public static void main(String[] args)
    {

    }

}

我不确定我完全理解了。你是想找出一个形状可以适合在一个矩形中的所有可能方式,还是找出有多少个可以适合在同一个网格上?我认为我可以为两者都设计出一个答案。 - Forseth11
我试图找到一个形状适合于一个5xn的矩形的所有可能方式,但为了达到这个目标,我首先尝试解决在一个固定的5x3网格中可以容纳多少个形状。因此,我希望能够听到两个答案=),这样我就可以学到更多知识。期待您的回复。 - Matthis Kohli
在看到这个评论之前,我已经发布了一个答案来找到有多少个合适的。为了找到所有可能性,您可以修改答案中的步骤2,循环遍历可能性并给出坐标。 - Forseth11
非常感谢,我会尽快开始阅读这个内容。真的非常感激。 - Matthis Kohli
这是在HHN的Heinz那里作为练习碰巧发生的吗? - Quatsch
2个回答

3
要找到一个五格多米诺骨牌放置在一个矩形内的所有可能方式,您可以按照以下步骤进行:
  1. 找到五格多米诺骨牌的长度和宽度,就好像您要画一个与其大小相同的矩形。
  2. 现在找出该矩形可以适合于较大矩形的次数。例如,您有一个3x5的矩形,而较小的矩形是W形状的,大小为3x3。 5-3 + 1 = 3。在3x5正方形中放置W形状有3种可能的方法(不翻转或旋转)。如果矩形是5x5,我们仍在使用相同的五格多米诺骨牌,则可以有9种不同的可能放置方式(不翻转或旋转)。 使用公式:x = a-b + 1; y = c-d + 1; xy = z(无旋转的可能方式)。其中a =大矩形的长度;b =小矩形的长度;c =大矩形的宽度;d =小矩形的宽度。
  3. 由于我们正在处理矩形,因此您需要翻转形状并旋转它,每次这样做时都要执行步骤3中的相同计算,并将其添加到总数中。
  4. 步骤3带来了另一个问题。如果翻转或旋转的形状与先前的形状相同怎么办?为了计算这一点,请以x和y的格式获取该形状,因此w形状将是:{(0,0),(1,0),(1,1),(2,1),(2,2)},如果原点在左上角。取这些坐标并旋转它们。(0,0)-〉(0,3)表示第一个点逆时针旋转90度。要旋转任何点,首先90度执行以下操作:(y,w-x-1),其中w =矩形的宽度。旋转下一个90度时执行:(y,h-x-1),其中h =矩形的高度。最后一次旋转重复(y,w-x-1)。请记住,每次旋转时必须交换宽度和高度,因为它是一个矩形而不总是正方形。如果任何这些旋转图形具有相同的坐标(它们不必按相同顺序排列),则将不使用步骤2检查该特定旋转。
  5. 最后,您必须处理反射/翻转。这比旋转更容易。要水平翻转,只需执行(w-x-1,y)即可。其中w =形状的宽度。

这是我用来解决和测试它的工作的一些图片:

找到可能性:

Find possibilities

enter image description here

enter image description here

旋转(正方形和矩形): enter image description here

enter image description here

翻转: enter image description here


还在思考……我不理解这里的Dancing-Links。我现在已经用节点填充了一个5xn的区域,但注意到它不是Dancing-Links。=( - Matthis Kohli
抱歉...我不知道5xn场是什么,也不知道Pentominos是什么。我只是根据你的问题和展示的内容以及逻辑来回答你的问题。 - Forseth11
你的公式中长度和宽度分别是多少?在一个3x5的矩形中,“W”是一个3x3的矩形。哪个是长度?列还是行? - Matthis Kohli
宽度指左右的单元格数量,而高度指上下的单元格数量。因此,宽度就是列数。在一个3x5的矩形中,3是宽度,5是高度。 - Forseth11

0

在标准的五格多米诺精确覆盖问题中,每个多米诺骨牌必须恰好使用一次;Knuth在https://arxiv.org/pdf/cs/0011047.pdf中提到(对于一个8x8的网格,中心减去4个单元格,这对于60个单元格的标准网格是正确的):

[...] 想象一个矩阵,它有72列,每个多米诺骨牌都有12个,每个单元格都有60个[...]。

...这是因为我们正在查看网格空间中60个位置的精确覆盖,以及五格多米诺名称空间中12个位置。

但在您的问题中,同一块多米诺骨牌可能会被多次使用。 在这种情况下,您不需要添加“列”来表示覆盖五格多米诺骨牌的需求:在您的5x4网格配置中,您需要20列。因此,您不需要在您的绘图中显示额外的W列,其中填满了1(否则问题将无法解决)。 您的其他列的初步查看并没有引起震惊。但是,您应该以编程方式生成这些内容。

然后,考虑到您的5x4网格配置,您需要有与固定五格多米诺变体在其可能位置上对应的行数(通常为376行,如果要考虑棋盘的对称性,则行数会更少)。

我将我的行名称设置为{pentomino-name}/{pentomino-variant}@{pentomino-origin-x},{pentomino-origin-y}

您提供的代码缺少以下内容:构建由1组成的矩阵、2D节点数组及其根据1矩阵的环形链接、列标题以及打印解决方案的方法。


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