用 1 和 -1 填充 n x n 零矩阵的方法,使得每行和每列只能有一个 1 和 -1,并且每行和每列的总和为 0

Posted

技术标签:

【中文标题】用 1 和 -1 填充 n x n 零矩阵的方法,使得每行和每列只能有一个 1 和 -1,并且每行和每列的总和为 0【英文标题】:Ways to fill n x n zero matrix with 1 and -1 so that there can only be one 1 and -1 per row and column and the sum of each row and column is 0 【发布时间】:2018-10-07 16:07:40 【问题描述】:

我正在寻找一种 F(n^3) 技术来找到所有可能的方法来使用以下规则填充 n x n 矩阵:每行和每列只能有一个 -1 和一个 1。每行和每列的总和必须为 0。N

我已经尝试过回溯,但由于输入的可能大小,它太慢了。 解决方案应该是 mod 10^9 - 7

【问题讨论】:

如果我没记错的话,应该有大约 (n!)^2 种方法可以做到这一点,因此需要相当长的时间。 哦,我忘了补充一下解决方案应该是 mod 10^9 - 7 如果这是一个编程竞赛,请提供一个链接。 【参考方案1】:

在输入这个答案时,我忘记了“F(n^3)”部分。也许从输入中提取“数字”(见下文)需要 n^3 算法。

这里要注意的主要一点是,您不应该生成所有可能的填充矩阵。输出只是一个数字。所以,如果你能找到一个合适的方程,那么这就是你的“算法”。

要注意的另一件事是,您的输入与那些非空单元格的所有位置都不相关。重要的是这九个数字:

单独 1 的数量(在行和列中都没有成对的 -1), solo -1 的数量, 单独行对的数量(其中 1 和 -1 在列中都不是成对的) 单列对数(与之前相同,但已转置) 空行数 空列数 错失 1 以形成成对四胞胎的成对三胞胎数 错过 -1 以形成成对四胞胎的成对三胞胎数 成对的四胞胎数

以下是这七个概念矩阵形式的案例,如果你不理解它们的书面形式,这将是没有意义的:

1 0  | -1 0 | 1 -1 |  1 0 | 0 0 | 0 |  1 -1 | -1 1 |  1 -1
0    |  0   | 0  0 | -1 0 |     | 0 | -1  0 |  1 0 | -1  1

您必须从输入中提取这 9 个数字中的前 8 个数字,这已经是一项不错的工作了。最后一个并不那么重要,因为那个四联体已经被填满并且“不可合并”。

您还必须弄清楚如何使用这八个数字。您基本上必须弄清楚哪些概念矩阵片段可以“合并”在一起以形成完整的矩阵片段(没有空格),然后找到可以用剩余的 1 和 -1 填充这些片段的方式的数量,然后将所有那些数字。

最后一部分是模运算,但这部分很简单。确保您前面只有乘法,并且在每次乘法之后应用模运算。最大乘积可以是 99999993*500 = 49999996500,大于最大 32 位整数,但不大于最大 64 位整数。所以这里最简单的做法是让所有操作都使用 64 位算术。

编辑:我刚刚意识到这个答案并不完全完整甚至不正确。我认为矩阵片段可以以简单直接的方式合并,只能成对合并,但可能会有无穷无尽的长条合并。 (许多独奏1可以合并在一起,而不仅仅是两个)。所以最终没有一个简单的等式。也许你仍然可以使用答案的概念,但只是需要更多的编码。

【讨论】:

【参考方案2】:

请注意,如果我们在每一列和每一行上都有一个 -1 和 1,并且有 2n 个,那么我们就满足了所有条件。

查找所有有 1 的索引。我们将数量称为 x。 我们需要放置 (n - x) 1. 有多少种方法可以做到这一点?让我们想象一下最初没有放置-1。让我们选择没有 1 的第一列。我们需要在那里放置一个 1。我们可以将它放置在 (n - x) 个可能的位置,因为我们不能将它放置在已经有 1 的某些行中。让我们选择没有 1 的第二列。我们可以将它放在 (n - x - 1) 个可能的位置,因为我们可以使用的行比第一列少 1。 概括这个想法,我们得出结论,我们有 (n - x ) * (n - x - 1 ) * (n - x - 2) ... * (1)。这是,(n - x)! 现在让我们考虑一下如果我们最初放置 -1 会发生什么。该公式的问题是我们没有考虑给定一列,我们不能将它放在我们已经有 -1 的单元格上。因此,根据列,我们可以少 1 个单元格,当该列有 -1 时,我们可以在其上放置 1。因此,我们需要将该公式更改为 (n - x - f(col)) * (n - x - 1 - f(col)) * ... 当有 -1 时为 f(col) = 1该列和 0 不是。这一直有效,直到最后一个公式,我们得到 * (1 - f(x))。问题出在最后一列,因为我们已经在所有其他列上放置了 1,所以只剩下 1 个选项可以放置 1,但是如果那个地方是我们有 -1 的地方,那么我们计数错误。这在其他列上不是问题,因为我们总是至少有 1 个空单元格可供使用。 因此,在需要 1 的最后一列已经放置了 -1 的情况下,我们不能将该位置作为最后一个选项。这意味着我们需要在该行前面的任何列上放置一个 1。如果最初在该行上放置了 1,我们无需担心。

要计算这个,我们可以这样做(需要添加memoization):

func count (idx, x, bool alreadyPlaced):
    if idx == last:
        return alreadyPlaced        
    if alreadyPlaced:
        return (n - x - f(idx)) * count(idx + 1, x + 1, alreadyPlaced)
    else:
        return (n - x - f(idx) - 1) * count(idx + 1, x + 1, false) + count(idx + 1, x + 1, true)

有了这个,我们就知道了在网格上放置 1 的方法的数量。现在剩下的就是计算放置 -1 的方式的数量并将这些 2 相乘。

将剩下的留作练习,但如果你无法弄清楚,请告诉我。

【讨论】:

感谢您的提示!我今天会调查一下,如果我设法解决它,请告诉你。 在计算 -1 时,我在考虑已经放置的 1 时遇到了一些问题。例如,如果我将 -1 放在第一行,如下所示:` 1|-1|0|0|0 \n 0| 1|0|0|0 ` 有 4 种可能的方式将 -1 放在第二行,而如果我将 -1 放在第一行,如下所示:` 1|0|-1|0|0\n 0 | 1|0|0|0 ` 只有三种方法可以将 -1 放在第二列。 有 1*4 + 3*3 种可能的方法吗? @dannasman 观察力不错。这也是我在数 1 时没有考虑过的问题。如果您有问题的链接,我可以尝试自己解决,以确保我正确处理所有情况 不是英文,但这里是输入和答案的示例:5 \n 0 0 0 0 0 \n 0 0 1 -1 0 \n 0 0 0 0 0 \n -1 0 0 0 0 \n 0 0 0 0-1 0 \n \n output: 16 第一个输入是 n,然后矩阵逐行插入,输出应该是答案 mod (10^9 - 7)。我已经设法制作了一个脚本,该脚本一直有效,直到大约 n = 10 put 对于较大的矩阵大小来说太慢了。这里有一些其他值可以帮助:。仅填充零的 5x5 矩阵 = 5280,可放置 1 的方式数 = 120,放置 1 后可放置的 -1 数量:44

以上是关于用 1 和 -1 填充 n x n 零矩阵的方法,使得每行和每列只能有一个 1 和 -1,并且每行和每列的总和为 0的主要内容,如果未能解决你的问题,请参考以下文章

[LeetCode] 130. Surrounded Regions

创建用 NaN 填充的 numpy 矩阵

数据挖掘(异常检测)——线性方法

如何在 Apache Commons 卡尔曼滤波器实现中填充矩阵

求问C++的Eigen矩阵运算库有没有提供两个矩阵对应元素相乘的方法

填充提升向量或矩阵