【问题标题】:Count frames in a 2D grid计算二维网格中的帧数
【发布时间】:2015-04-18 08:18:49
【问题描述】:

给定一个由 0 和 1 组成的 [NxM] 矩阵。找出这个矩阵中没有 1 的方框数(每个单元格为 0)。固定正方形的框架是一组单元格,每个单元格都是最左侧、最右侧、最顶部或最底部的单元格之一。因此,边长为 1 的方形框架包含 1 个单元格,边长 2 包含 4 个单元格,边长为 3 - 8 个单元格,边长为 4 - 12 个单元格。

大小为 4 的示例框架是:

0000
0  0
0  0
0000

我们需要计算仅由 0 组成的方框的数量。

例如让N=3M=4 和矩阵是:

0100
0100
0000

那么答案是 12,因为有 10 个一号帧和 2 个二号帧。

主要问题是 N,M 可以达到 1 ≤ N, M ≤ 2000 。所以需要O(N^2)方法。

【问题讨论】:

  • 请向我们展示您尝试过的代码?
  • 仅供参考 - 在大小为 m*n 且只有零的矩阵中,有 k*m*n - (k-1)*k/2*(m + n) + (k-1)*k(2*k-1)/6 帧,其中 k = min(m,n)

标签: algorithm matrix


【解决方案1】:

每一帧都可以等同于矩阵的一对元素——对角。假设左上和右下。它们必须在同一条对角线上,很明显。首先请注意,如果例如。 ((1;1);(5;5)) 是我们的框架,元素 (1;1)(5;5) 必须为空。此外,(1;1) 的右侧和下方至少有四个空值。从(5;5) 向左和向上相同。因此,第一个想法是计算每个元素 x 与他自己向下和从左向上的最小空值(线性时间)。

如果我没记错的话,就是例子:

-->(x;y) = (min(right, down); min(left,upward))

00000    (5;1)(1;1)(1;1)(2;1)(1;1)
01101    (1;1)(0;0)(0;0)(1;1)(0;0)
01001--->(1;1)(0;0)(2;1)(1;2)(0;0)
00000    (2;1)(1;1)(1;2)(1;4)(1;1)
01110    (1;1)(0;0)(0;0)(0;0)(1;1)

第二个思路:分别分析每条对角线,从左上角到右下角。

  1. (1;1)
  2. (2;1)(0;0)
  3. (1;1)(1;1)(0;0)
  4. 等等……

您需要一些结构 Q 来表示对,这将允许您:

  • 擦除后继元素小于... O(lg n)
  • 计算前导大于... O(lg n) 的元素
  • 添加对。 O(lg n)

将全局结果设置为空。 每条对角线的算法都很简单。取空​​ Q。迭代以下元素,i = 0,1,2,...。对于每个:

  • 从 Q 元素中删除,其中后继元素小于 i。 (它可能是唯一具有最小后继元素的元素。)
  • 如果实际元素不是 (0;0):
    • 推对,前任:i,后继:最后一个元素的数量与实际元素可以配对。它是i+x_i-1,其中x_i 是实际元素的min(right, down)
    • 计算 Q 中前任元素大于 i-y_i 的元素。这些是可以与实际元素配对的元素。将该数字添加到“全局”结果中。

在全局结果中,您有答案。这是 O(NM log N) 算法,我不确定,如果你能做得更快。


对角线的示例。我们有 table=((5;1),(0;0),(2;1),(1;4),(1;1)) 来分析。

Generally for element:
   Current element: (x;y); i=i           // O(1)
   Q.erase_smaller_than(i)              // O(lg N)
   if (x;y) == (0;0) :                  // O(1)
        skip element
   Q.emplace(i, i+x-1)                  // O(lg N)
   result += Q.count_bigger_than(i-y)   // O(lg N)


For diagonal example:

0. Q = {}
   result = 0
   for element in ((5;1),(0;0),(2;1),(1;4),(1;1))

1. Current element: (5;1); i = 0
   Q.erase_smaller_than(i)
   Q.emplace(i, i+5-1) // (0; 4)
   temp = Q.count_bigger(i-1) //taking only predecessor
   // bigger than -1, 1. element (frame with only one element)
   result += temp // result = 1

2. Current element: (0;0); i = 1
   Q.erase_smaller_than(i) //nothing changed
   (0;0) element, skip

3. Current element: (2;1); i = 2
   Q.erase_smaller_than(i) //nothing changed, Q = {(0;4)}
   Q.emplace(i, i+2-1) // (2;4)
   temp = Q.count_bigger(i-1) // only current element
   result += temp // result = 2

4. Current element (1;4); i = 3
   Q.erase_smaller_than(i) //Q = {(0;4)(2;4)}
   Q.emplace(i; i+1-1) // (3; 3)
   temp = Q.count_bigger(i-4) //all three elements from Q
   result += temp // result = 5\

5. Current element (1;1); i = 4
   Q.erase_smaller_than(i) // Q = {}
   Q.emplace(i;i+1-1) // (4;4)
   temp = Q.count_bigger(i-1) // only current element
   result += 1

6. End of loop. Print "On main diagonal $result frames have corners.".
   Continue that algorithm for next diagonal lines.

【讨论】:

  • 我不懂你的算法。它是否将帧一一添加到“全局”结果中,或者有时会一次将多个帧添加到“全局”结果中?你能在一个小例子上展示它是如何工作的,或者发布一些代码吗?
  • @גלעדברקן 对于每个可以计算的元素,该元素可以在 O(lg N) 时间内右下角的帧数。 (例如,查看 2D 树。)我正在使用它来计算所有帧。 (我不是一一添加帧。我只知道帧数。)我添加了一个示例,希望现在清楚。如果这是问题,抱歉语言不好。
  • 感谢您添加示例,它有助于我更好地理解。如何在线性时间内制作(min(right, down); min(left,upward)) 的完整表格?
  • 如果你在计算“Q 中具有更大前任的元素”,这是否意味着程序正在逐帧计算?在 2000x2000 的零矩阵中,有 2,668,667,000 帧。真的实用吗?
  • @גלעדברקן 我只是在迭代,每行和每列两次。我不想编写复制过去的代码或仅用于 C++ 编码器,因此here 是可读的(我希望)示例,这是正确的最低限度。最低限度也一样。 right-down 最小值是这两个值中的最小值。不,它没有。令我难过的是,可以在对数时间内计算所有大于某个值的元素。所以在 2000x2000 矩阵中,我需要 2000 x 2000 x lg 2000 ~= 43 863 138 操作来计算所有帧。我认为这很实用。另外我不确定是否存在更好的解决方案,是吗?
【解决方案2】:

我用 c# 中的代码解释我的算法:

static void Main(string[] args)
{
    int[,] ar = new int[,] { {0,1,0,0}, 
                             {0,1,0,0}, 
                             {0,0,0,0} };

    int count = 0; // Count of square matrices
    int sum = 0;   // A temporary variable for checking validation of a square matrices

    int max = 0;   // Max size of square matrices for each point

    for (int i = 0; i < ar.GetLength(0) ; i++)
    {
        for (int j = 0; j < ar.GetLength(1); j++)
        {
            if (ar[i, j] == 0)
            {
                //calculate maximum size of square Matrices
                max = ar.GetLength(0) - i;
                if (max > ar.GetLength(1) - j)
                    max = ar.GetLength(1) - j;

                //Search kxk square matrice
                for (int k = 0; k < max; k++)
                {
                    sum = 0;
                    // Search Borders
                    for (int l = 1; l <= k; l++)
                    {
                        sum += ar[i + l, j];
                        sum += ar[i, j + l];
                        sum += ar[i + l, j + k];
                        sum += ar[i + k, j + l];
                        if (sum > 0)
                            break;
                    }
                    sum += ar[i + k, j + k];

                    if (sum == 0)
                        count++;
                }
            }
        }
    }

    Console.WriteLine("{0}", count.ToString());
    Console.ReadKey();
}

我使用sum 变量发现它所有的边界点都是0。

【讨论】:

  • @ho_dareago 如果我没有错过任何东西,那就是 O(MN^3)。纠正我,如果我错了。
【解决方案3】:

一个框架总是有一个左上角0。因此,您可以遍历矩阵(对于 N 和内部 M),并在每次迭代中计算左上角元素为 0 的所有帧。在您的示例中:

0100
0100
0000
  1. 对于N=1M=1,您可以找到1 框架。
  2. 接下来是N=1M=2。你可以找到0 框架。
  3. 对于N=1M=3,您可以找到2

等等……

【讨论】:

  • 不过这需要 O(N^3)。
猜你喜欢
  • 2015-11-14
  • 1970-01-01
  • 2021-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多