最快的实现,将n×n板切割成n个连接的n-minos

时间:2017-05-24 15:33:27

标签: complexity-theory graph-theory

我正在尝试制作一个谜题,让玩家使用n个连接的n-minos将n×n网格拼凑在一起(定义:连接的n个1×1块,例如每个俄罗斯方块片段是4-mino) 。然而,尽管看起来对人类来说似乎很容易,但是生成切割网格的方法首先证明是一种挑战。

example board示例板

对于人类来说,通过递归地遵循以下逻辑/伪代码来生成这样的解决方案是相对容易的任务:

:start_of_recursion:

  • 从一个随机的“连接最少”的部分(结尾,角落,连接最少成员的边缘部分)开始作为起始的mino块

    :start_of_recursion:

    • 从当前mino中的随机片段随机可用方向“生长”
      • 如果“增长”导致剩余的“分离”板(如果分隔区域不是n的倍数),请尝试其他位置和方向
        • 如果尝试了所有位置和方向,请恢复到之前的电路板配置(不应该真的发生?)
    • 如果已达到size-n,则退出递归

    :end_of_recursion:

  • 如果已填写董事会,则退出递归

:end_of_recursion:

执行此例程似乎生成了一种O(n ^ 2)解决方案生成方法,但条件检查证明对计算机来说确实很昂贵。为了确定是否要连接电路板,人类只是检查剩余区域内的任何“间隙”,并以几乎O(1)的方式处理简单的非重叠图,而我的代码实现需要“从图表上的一个点扩散到其邻近地区,并在扩散完成后进行检查,以检查是否有任何点位于可达范围之外(O(n)最多)。由于每次在最里面的迭代中都会执行此检查,因此它会将复杂性退化为O(n ^(3+))问题,并且变得非常低效。

是否有一种方法以类似于人类认知的方式检查“缺口”?或者可以从根本上考虑问题并简化为计算机更容易解决的问题?

1 个答案:

答案 0 :(得分:0)

您的问题听起来像bin packing problem的变体。我会通过约束满足方法来解决这个问题。下面我将使用Minizinc伪代码。

电路板由电池组成,每个电池可以从几个颜色中分成一种颜色。我们可以表示如下:

int: rows;
int: cols;
int: colors_num;

array [1...colors_num] of int: colors;
array [1..rows,1..cols] of var colors: board;

接下来,我们添加约束。例如,如果单元格具有颜色A,则至少1个相邻单元格必须具有相同的颜色A:

constraint forall (c in colors) (
  if board[i, j] == c then
    at_least (1, [board[i-1, j], board[i+1, j], board[i, j-1], board[i,j+1]], c)
  else
    true
  endif

您可以将所有允许/禁止的形状描述为约束,或使用其他智能方法引入可能的切割。

约束满意度应该 比递归方法更有效。然而,它不是很可扩展 - 如果你试图为一个巨大的棋盘(数百或数千个细胞/颜色)生成游戏,生成迷你将需要相当长的时间和记忆。