每一帧都可以等同于矩阵的一对元素——对角。假设左上和右下。它们必须在同一条对角线上,很明显。首先请注意,如果例如。 ((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)
- (2;1)(0;0)
- (1;1)(1;1)(0;0)
- 等等……
您需要一些结构 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.