“填充网格”问题


36

具有简单规则但非平凡算法的挑战。:-)

任务

以空格分隔的整数形式输入:

N A B S

其中N是2D正方形矩阵的边长,其中A和B之间(包括两端)填充有唯一数字(整数)。对于此矩阵中的每一行和每一列,总和始终相同:S。(换句话说,矩阵是一个半魔术正方形)。

注意:

所有数字均为正。例外是A,可以为0。

例子

对于

3 1 10000 2015

一个有效的解决方案是

在此处输入图片说明

对于

8 1 300 500

一个有效的解决方案是

在此处输入图片说明

输出量

您输出的应该是ASCII表。上面第一个示例的示例:

 384  159 1472
1174  499  342
 457 1357  201

用空格填充的右对齐整数。每列的宽度是该列中最大整数的宽度。

计分

这是,因此以字节为单位的最短代码获胜。存在标准漏洞(尤其是有关解决此问题的内置漏洞)。您不必担心错误或其他不可能的输入(包括负数)。请为上面的第二个示例在您的答案中(强制性)提供示例输出。


1
允许我们在A和B之间生成随机数,直到它们正确加总并且是唯一的?
lirtosiast,2015年

只是为了检查,AB,和N可以是负数?
xnor 2015年

2
minxomat,我并不是说这是我能想到的最好的解决方案,而是说它可能是最短的。
lirtosiast 2015年

3
@LuisMendo您必须根据任务生成示例输出。如果您能够在一生中用蛮力的方法来解决这个问题,我会印象深刻。:-)。我可以排除它,但是它太模糊了,因为最流行的解决方案(GA)仍然涉及随机性。另外,当有人可能已经在解决方案上工作时,我也不想更改规则。
mınxomaτ

1
@minxomat您所有的三个论点都很好:-)
Luis

Answers:


19

CJam,119 91字节

q~:M;),>:R;(:L{{R{ML)d/-Y#)mr}$L/L<2{{M1$:+-+}%z}*:U:+__O|=R*-}gU{:s_:,:e>f{Se[}}%zSf*N*}M?

这是一种可以证明是正确的,不确定的方法。

在我的桌面上,第二个测试用例通常会在不到10分钟的时间内完成。

第一种情况立即完成。在CJam解释器中在线尝试。

样品运行

$ cjam grid.cjam <<< '8 1 300 500'
77  66  37 47  56  46 86  85
63 102  70 72  49  54 81   9
62  69  58 57  71  17 48 118
64  65  67 87  53  44 80  40
73  60  55 89  51  76 84  12
68  59  28 78  74  38 50 105
61  75  52 43 125  83 42  19
32   4 133 27  21 142 29 112

理念

没有时间限制,我们可以随机生成正方形,直到找到有效的正方形。这种方法基于该思想,并添加了两个优化:

  • 代替伪随机生成边长为N的正方形,我们生成边长为N-1的正方形,添加一列以形成N×(N-1)个矩形,其行的总和为S,然后一行形成一个平方的正方形边长ñ其列有总和小号

    由于所有列的元素的总和将是NS和第一的元素之和N-1行是(N-1)s,在最后一行也将有总和小号

    但是,此过程可能会生成无效的矩阵,因为不能保证最后一行和最后一列的所有元素都是唯一的或落在[A ... B]范围内。

  • 随机选择[A ... B]和边长N-1的唯一整数的平方会花费太长时间。在应用上一个项目符号要点中详细介绍的过程之后,我们必须以某种方式优先考虑那些更有可能导致边长为N的有效平方的平方。

    假定每个行和列必须具有的总和小号,其元素具有的平均S / N。因此,选择更多接近该平均值的元素将增加我们的机会。

    对于每个[A ... B] ,我们伪随机挑之间的浮子0(I - S / N)2 + 1和排序的元素[A ... B]由拾取浮动。我们保留前N 2个数字,并将它们按阅读顺序放在一个正方形中。

    假设每一步中0(I-S / N)2 +1之间的所有实数的分布完全均匀,则所有正方形的被拣选概率都为非零,这意味着该过程将最终完成。

q~          e# Read all input from STDIN and evaluate it.
:M;         e# Save "S" in M and discard it from the stack.
),>:R;      e# Transform "A B" into [A ... B], save in R and discard.
(:L         e# Save "N - 1" in L and keep it on the stack.
{           e# If L is non-zero:
  {         e#   Do:
    R{      e#     For each I in R:
      ML)d/ e#       Compute M/Double(L+1).
      -Y#   e#       Subtract the result from I and square the difference.
      )mr   e#       Add 1 and pick a non-negative Double below the result.
    }$      e#     Sort the values of I according to the picks.
    L/      e#     Split the shuffled R into chunks of length L.
    L<      e#     Keep only the first L chunks.
    2{      e#     Do twice:
      {     e#       For each row of the  L x L array.
        M1$ e#       Push M and a copy of the row.
        :+- e#       Add the integers of the row and subtract their sum from M.
        +   e#       Append the difference to the row.
      }%    e#
      z     e#       Transpose rows and columns.
    }*      e#
    :U:+    e#     Save the result in U and concatenate its rows.
    __O|    e#     Push two copies. Deduplicate the second copy.
    =R*     e#     Push R if all elements are unique, an empty array otherwise.
    -       e#     Remove the result's elements from U's elements.
  }g        e#   If the resulting array is non-empty, repeat the loop.
  U{        e#   For each row in U:
    :s      e#     Convert its integers into strings.
    _:,     e#     Copy and replace each string with its length.
    :e>     e#     Compute the maximum length.
    f{      e#     For each integer, push the maximum length; then
      Se[   e#       Left-pad the integer with spaces to that length.
    }       e#
  }%        e#
  z         e#   Transpose rows with columns.
  Sf*N*     e#   Join columns by spaces, rows by linefeeds.
}M?         e# Else, push M.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.