具有相同编号的最大矩形子矩阵

时间:2011-10-14 16:55:21

标签: algorithm matrix dynamic-programming

我正在尝试提出一种动态编程算法,该算法在矩阵中找到由相同数字组成的最大子矩阵:

示例:

{5 5 8}
{5 5 7}
{3 4 1}

答案:由矩阵引起的4个元素

   5 5 
   5 5   

4 个答案:

答案 0 :(得分:25)

这个问题我已经回答here(和here,修改后的版本)。在这两种情况下,算法都应用于二进制大小写(零和1),但对任意数字的修改非常容易(但很抱歉,我保留图像的二进制版本的问题)。您可以通过两次线性O(n)时间算法非常有效地完成此操作 - n是元素数量。然而,这不是一个动态编程 - 我认为在这里使用动态编程最终将是笨拙和低效的,因为问题分解的困难,正如OP所提到的 - 除非它是一个功课 - 但在这种情况下你可以尝试这个算法给人留下深刻印象:-)因为显然没有比O(n)更快的解决方案。

算法(图片描绘二进制大小写)

假设你想找到最大的自由(白色)元素矩形。

enter image description here

以下是两次线性O(n)时间算法(n为elemets数):

1)在第一遍中,按列从下到上依次为每个元素表示可用的连续元素数量:

enter image description here

重复,直到:

enter image description here

图片描绘了二进制案例。在任意数字的情况下,您可以容纳2个矩阵 - 首先是原始数字,第二个是上面图像中填充的辅助数字。您必须检查原始矩阵,如果找到与前一个不同的数字,则只需从1开始编号(在辅助矩阵中)。

2)在第二次传递中你按行进行,保持潜在矩形的数据结构,即包含位于顶部边缘某处的当前位置的矩形。见下图(当前位置为红色,3个可能的矩形 - 紫色 - 高度1,绿色 - 高度2和黄色 - 高度3):

enter image description here

对于每个矩形,我们保持其高度k及其左边缘。换句话说,我们跟踪>= k的连续数字的总和(即高度k的潜在矩形)。此数据结构可以由一个数组表示,该数组具有链接占用项的双链表,并且数组大小将受矩阵高度的限制。

第二遍的伪代码(具有任意数字的非二进制版本):

var m[] // original matrix
var aux[] // auxiliary matrix filled in the 1st pass
var rect[] // array of potential rectangles, indexed by their height
           // the occupied items are also linked in double linked list, 
           // ordered by height

foreach row = 1..N // go by rows
    foreach col = 1..M
        if (col > 1 AND m[row, col] != m[row, col - 1]) // new number
            close_potential_rectangles_higher_than(0);  // close all rectangles

        height = aux[row, col] // maximal height possible at current position

        if (!rect[height]) { // rectangle with height does not exist
            create rect[height]    // open new rectangle
            if (rect[height].next) // rectangle with nearest higher height
                                   // if it exists, start from its left edge
                rect[height].left_col = rect[height].next.left_col
            else
                rect[height].left_col = col; 
        }

        close_potential_rectangles_higher_than(height)
    end for // end row
    close_potential_rectangles_higher_than(0);
        // end of row -> close all rect., supposing col is M+1 now!

end for // end matrix

关闭矩形的功能:

function close_potential_rectangles_higher_than(height)
    close_r = rectangle with highest height (last item in dll)
    while (close_r.height > height) { // higher? close it
        area = close_r.height * (col - close_r.left_col)
        if (area > max_area) { // we have maximal rectangle!
            max_area = area
            max_topleft = [row, close_r.left_col]
            max_bottomright = [row + height - 1, col - 1]
        }
        close_r = close_r.prev
        // remove the rectangle close_r from the double linked list
    }
end function

这样你也可以得到所有最大的矩形。所以最后得到:

enter image description here

复杂性是多少?您会看到每个关闭的矩形函数close_potential_rectangles_higher_thanO(1)。因为对于每个字段,我们最多创建1个潜在的矩形,特定行中存在的潜在矩形的总数永远不会高于行的长度。因此,此函数的复杂性为O(1)摊销!

因此整个复杂度为O(n),其中n是矩阵元素的数量。

答案 1 :(得分:3)

动态解决方案:

定义一个新的矩阵A,它将以A[i,j]两个值存储:最大子矩阵的宽度和高度,左上角位于i,j,填充此矩阵从右下角,按行从下到上。你会发现四种情况:

案例1 :原始矩阵中的右边或底边邻居元素都不等于当前矩阵,即:M[i,j] != M[i+1,j] and M[i,j] != M[i,j+1]M原始矩阵,在这种情况下,A[i,j]的值为1x1

case 2 :右边的邻居元素等于当前的邻居元素,但是底部的邻居元素不同,A[i,j].width的值是A[i+1,j].width+1和{{1 }}

案例3 :底部的邻居元素相同但右边的元素不同,A[i,j].height=1

案例4 :两个邻居都是平等的:A[i,j].width=1, A[i,j].height=A[i,j+1].height+1A[i,j].width = min(A[i+1,j].width+1,A[i,j+1].width)

A[i,j].height = min(A[i,j+1]+1,A[i+1,j])左上角的最大矩阵的大小为i,j,因此您可以在计算A[i,j].width*A[i,j].height

时更新找到的最大值

底部行和最右边的列元素被视为它们的底部和右侧的邻居分别是不同的

在您的示例中,生成的矩阵A[i,j]将为:

A

{2:2 1:2 1:1} {2:1 1:1 1:1} {1:1 1:1 1:1} w:h

答案 2 :(得分:0)

修改上述答案:

定义一个新矩阵A将存储在A [i,j]中的两个值:最大子矩阵的宽度和高度,左上角位于i,j,从右下角开始填充此矩阵,按行从下到上。你会发现四种情况:

情况1:原始矩阵中的右或底相邻元素都不等于当前矩阵,即:M [i,j]!= M [i + 1,j]和M [i,j] != M [i,j + 1]是原始矩阵的M,在这种情况下,A [i,j]的值是1x1

情况2:右边的邻居元素等于当前的邻居元素,但是底部的邻居元素不同,A [i,j] .width的值是A [i + 1,j] .width + 1和A [I,J] .height = 1

情况3:底部的邻居元素相等但右边的元素不同,A [i,j] .width = 1,A [i,j] .height = A [i,j + 1]。高度+ 1

案例4:两个邻居都是平等的:         考虑三个矩形: 1. A [i,j] .width = A [i,j + 1] .width + 1; A [I,J] .height = 1;

  1. A [I,J] .height = A [1 + 1,j]的.height + 1;一个[I,J] .WIDTH = 1;

  2. A [i,j] .width = min(A [i + 1,j] .width + 1,A [i,j + 1] .width)和A [i,j]。高度= min(A [i,j + 1] + 1,A [i + 1,j])

  3. 在上述三种情况下具有最大面积的那一个将被视为代表该位置的矩形。

    在i,j左上角的最大矩阵的大小是A [i,j] .width * A [i,j]。高,所以你可以更新计算A时找到的最大值[ I,J]

    底部行和最右边的列元素被视为它们的底部和右边的邻居分别不同。

答案 3 :(得分:0)

这个问题是duplicate。我试图将其标记为重复。这是一个Python解决方案,它还返回最大矩形子矩阵的位置和形状:

#!/usr/bin/env python3

import numpy

s = '''5 5 8
5 5 7
3 4 1'''

nrows = 3
ncols = 3
skip_not = 5
area_max = (0, [])

a = numpy.fromstring(s, dtype=int, sep=' ').reshape(nrows, ncols)
w = numpy.zeros(dtype=int, shape=a.shape)
h = numpy.zeros(dtype=int, shape=a.shape)
for r in range(nrows):
    for c in range(ncols):
        if not a[r][c] == skip_not:
            continue
        if r == 0:
            h[r][c] = 1
        else:
            h[r][c] = h[r-1][c]+1
        if c == 0:
            w[r][c] = 1
        else:
            w[r][c] = w[r][c-1]+1
        minw = w[r][c]
        for dh in range(h[r][c]):
            minw = min(minw, w[r-dh][c])
            area = (dh+1)*minw
            if area > area_max[0]:
                area_max = (area, [(r, c, dh+1, minw)])

print('area', area_max[0])
for t in area_max[1]:
    print('coord and shape', t)

输出:

area 4
coord and shape (1, 1, 2, 2)