Самая большая прямоугольная подматрица с тем же номером - PullRequest
14 голосов
/ 14 октября 2011

Я пытаюсь найти алгоритм динамического программирования, который находит наибольшую субматрицу в матрице, состоящей из того же числа:

пример:

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

Ответ: 4элементы из-за матрицы

   5 5 
   5 5   

Ответы [ 4 ]

26 голосов
/ 15 октября 2011

На этот вопрос я уже ответил здесь здесь , измененная версия). В обоих случаях алгоритм был применен к двоичному случаю (нулям и единицам), но модификация для произвольных чисел довольно проста (но извините, Я сохраняю изображения для двоичной версии задачи ). Вы можете сделать это очень эффективно с помощью двухпроцессорного алгоритма linear O(n) time - n равно числу элементов. Тем не менее, это не динамическое программирование - я думаю, что использование динамического программирования здесь было бы неуклюжим и неэффективным в конце концов, из-за трудностей с декомпозицией проблемы, как упоминалось в OP - если только это не домашнее задание - но в этом случае вы можете попытаться впечатлите этим алгоритмом :-), поскольку, очевидно, нет более быстрого решения, чем O(n).

Алгоритм (изображения изображают двоичный регистр) :

Допустим, вы хотите найти самый большой прямоугольник из свободных (белых) элементов.

enter image description here

Здесь следует алгоритм двухпроходного линейного O(n) времени (n - количество элементов):

1) в первом проходе , пройти по столбцам снизу вверх, и для каждого элемента обозначить количество последовательных элементов, доступных до этого:

enter image description here

повтор, до:

enter image description here

Картинки изображают двоичный регистр. В случае произвольных чисел вы держите 2 матрицы - сначала с исходными числами, а затем с вспомогательными числами, которые заполнены на рисунке выше. Вы должны проверить исходную матрицу, и если вы найдете число, отличное от предыдущего, вы просто начнете нумерацию (во вспомогательной матрице) снова с 1.

2) во втором проходе вы идете по строкам, хранящим структуру данных потенциальных прямоугольников, то есть прямоугольников, содержащих текущую позицию где-то у верхнего края. Смотрите следующее изображение (текущая позиция красного цвета, 3 потенциальных прямоугольника - фиолетовый - высота 1, зеленый - высота 2 и желтый - высота 3):

enter image description here

Для каждого прямоугольника мы сохраняем его высоту k и его левый край. Другими словами, мы отслеживаем суммы последовательных чисел, которые были >= k (то есть потенциальные прямоугольники высотой k). Эта структура данных может быть представлена ​​массивом с двойным связанным списком, связывающим занятые элементы, а размер массива будет ограничен высотой матрицы.

Псевдокод 2-го прохода (недвоичная версия с произвольными числами):

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_than равна O(1) на замкнутый прямоугольник. Поскольку для каждого поля мы создаем 1 потенциальный прямоугольник в максимуме, общее количество потенциальных прямоугольников, когда-либо присутствующих в конкретной строке, никогда не превышает длину строки. Поэтому сложность этой функции O(1) амортизируется!

Таким образом, вся сложность равна O(n), где n - количество элементов матрицы.

3 голосов
/ 15 октября 2011

Динамическое решение:

Определите новую матрицу A, которая будет хранить в A[i,j] два значения: ширину и высоту наибольшей подматрицы с левым верхним углом в i,j, заполните эту матрицу, начиная с нижнего правого угла, строки снизу вверх. Вы найдете четыре случая:

case 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 и A[i,j].height=1

case 3 : соседний элемент снизу равен, но правый отличается, A[i,j].width=1, A[i,j].height=A[i,j+1].height+1

вариант 4 : оба соседа равны: A[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 width:height

0 голосов
/ 24 мая 2015

Этот вопрос является дубликатом . Я попытался пометить его как дубликат. Вот решение 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)
0 голосов
/ 15 июля 2014

Модификация к ответу выше:

Определить новую матрицу 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] .height + 1

случай 4: оба соседа равны: рассматриваются три прямоугольника: 1. A [i, j] .width = A [i, j + 1] .width+1;A [i, j] .height = 1;

  1. A [i, j] .height = A [i + 1, j] .height + 1;a [i, j] .width = 1;

  2. A [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]

, нижняя строка и крайние правые элементы столбца обрабатываются так, как если бы ихсоседи снизу и справа соответственно разные.

...