Динамическое программирование: максимальная сумма через двумерную матрицу (более сложная версия) - PullRequest
0 голосов
/ 10 мая 2018

Это из моего класса алгоритмов, и я думаю, что мне действительно нужна помощь.

Учитывая матрицу затрат Happiness [] [], где Happiness [i] [j] обозначает счастье посещения ячейки с координатами (i, j), вы начинаете с верхнего правого угла и заканчиваете в левом нижнем углу. Мы хотим найти максимальное счастье.
Вы можете только идти вниз или идти налево в матрице.

Но у нас есть 2 ограничения:
1. Вы можете пропустить ноль или более строк, что означает, что вы можете сначала перейти к последней строке, если хотите.
2. Когда вы решите идти налево, стоимость = стоимость - 1. Второй левый подряд вызывает стоимость = стоимость - 2, но когда вы идете вниз, то идите налево, он обновляется. Вот пример: enter image description here

Это шаг:
enter image description here

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

Мой рецидив: (я застрял здесь) enter image description here Я думаю, что базовые случаи - самый левый ряд и самый нижний столбец.
Как компьютер может проверить последовательные шаги и узнать, какое решение выбрать?

Я знаю, что странно публиковать весь вопрос, но я не могу упростить вопрос лучше ...

1 Ответ

0 голосов
/ 10 мая 2018

В соответствии с комментарием @ גלעד ברקן, хитрость в решении этой проблемы заключается в добавлении дополнительного измерения, представляющего количество левых, взятых для достижения ячейки, в рекуррентное отношение.

Примечание: чтобы облегчить эту проблему, я перевернул строки данной матрицы так, чтобы разрешенные направления движения были уменьшены и вправо , а начальная ячейка стала (0, 0), а конечная ячейка стала (W - 1, H - 1), где W и H - ширина и высота матрицы соответственно. Кроме того, матрица и соответствующая таблица максимального счастья dp считаются в главном порядке строк, то есть matrix[y][x] - это значение в ячейке (x, y).

Наш true базовый случай - это счастье посещения начальной ячейки, которое либо matrix[0][0], либо 0, в зависимости от того, должно ли значение начальной ячейки быть включено в общее счастье , Однако у нас также есть два псевдо базовых случая, которые проще вычислить по сравнению с реальной рекурсией.

Эти псевдо базовые случаи - это крайний левый столбец (помните, что строки были перевернуты, так что это столбец крайний правый в исходной матрице) и самый верхний ряд. Отношение повторения для ячейки (0, y) (1 <= y < H) в крайнем левом столбце:

h[y][0][0] = matrix[y][0] + max(h[i][0][0] for i in 0 to y-1)

, где h - это наша таблица максимального счастья, а h[y][x][r] - максимальное счастье, полученное при переходе от клетки (0, 0) к (x, y) (в соответствии с данными правилами), получив r последовательных прав (или левых, для исходной матрицы без перевернутой строки) непосредственно перед тем, как достигнет (x, y). Таким образом, если r равно 0, последняя пройденная ячейка была одна непосредственно над текущей ячейкой. Единственный возможный способ добраться до ячейки в крайнем левом столбце - это выйти из ячейки прямо над ней.

Важно отметить, что в нашей таблице максимального счастья максимальное счастье для любой ячейки (x, y) дается максимумом h[y][x][r] для всех значений r. Если h является трехмерным массивом / списком, его также можно представить как max(h[y][x]).

Аналогично, отношение повторения для ячейки (x, 0) (1 <= x < W) в самой верхней строке:

h[0][x][x] = h[0][x-1][x-1] + matrix[0][x] - x

Это связано с тем фактом, что единственный способ добраться до ячейки в верхнем ряду - продолжать движение прямо от начальной ячейки. Количество прав, полученных для достижения ячейки в верхнем ряду, равно ее X-координате на основе 0, следовательно, штраф -x.

Теперь, когда у нас есть базовые случаи, мы можем перейти к рекуррентному отношению для ячейки (x, y), где 1 <= x < W и 1 <= y < H.

При установлении этого рекуррентного отношения, первое, что нужно отметить, это то, что есть только две возможности: либо последняя ячейка была над или слева от данной ячейки. Мы рассмотрим обе эти возможности.

Если бы последняя ячейка была выше данной ячейки, она имела бы координаты (x, j), где 0 <= j < y. Для максимального счастья счастье в последней клетке также должно быть максимальным. Таким образом, максимальное счастье в (x, y), когда последняя ячейка находится над данной ячейкой, определяется как

h[y][x][0] = matrix[y][x] + max(max(h[j][x]) for j in 0 to y-1)

Обратите внимание, что количество сделанных прав (r) равно 0, а max(h[j][x]) - это максимальное счастье, полученное при достижении ячейки (x, j) с любым количеством прав.

Еслипоследняя ячейка была слева от данной ячейки (или, другими словами, последний ход был вправо), ее координаты были бы просто (x - 1, y).Это потому, что мы можем перемещать только одну ячейку вправо за раз.В отличие от предыдущего случая, последняя позиция ячейки является постоянной.Однако количество последовательных прав (r) , полученных для достижения этой ячейки, является переменным.Поэтому мы должны рассмотреть все возможные значения для r.Поскольку может быть сделано минимум 1 право и максимум x прав, 1 <= r <= x.Для каждого значения r максимальное счастье этой ячейки - это максимальное счастье ячейки слева (последняя ячейка) для прав r - 1 плюс значение в (x, y) минус штраф (который составляет всего r - 1).В псевдокоде

for r in 1 to x:
    h[y][x][r] = h[y][x-1][r-1] + matrix[y][x] - r

Теперь у нас есть все, что нужно для запуска алгоритма в восходящем или нисходящем подходе.Наш окончательный ответ, максимальное счастье, необходимое для достижения нижней правой (слева-вниз, для исходной матрицы, не перевернутой строкой), определяется как max(h[H-1][W-1]).

Собрав все вместе, мы получимследующий (Python-esque) восходящий алгоритм с временной сложностью алгоритма O(W^2 * H^2):

h[0][0][0] = matrix[0][0]

for x in range(1, W):
    h[0][x][x] = h[0][x-1][x-1] + matrix[0][x] - x

for y in range(1, H):
    h[y][0][0] = matrix[y][0] + max(h[i][0][0] for i in range(y))

for y in range(1, H):
    for x in range(1, W):
        for r in range(1, x + 1):
            h[y][x][r] = h[y][x-1][r-1] + matrix[y][x] - r
        h[y][x][0] = matrix[y][x] + max(max(h[j][x]) for j in range(y))

return max(h[H-1][W-1])

При ближайшем рассмотрении оказывается, что операция O(W) вычисления max(h[j][x]) может быть удаленапредварительно рассчитав его, сделав окончательную сложность по времени O(W * H^2).Обратите внимание, что эта оптимизация действительна только для восходящего подхода.Это работает, потому что ячейка (x, j) (где j < y) уже должна быть посещена при подходе снизу вверх.В приведенном ниже коде max(h[y][x]) хранится в h[y][x][W].

Полная оптимизированная версия восходящего алгоритма в Python 3 (включая данную примерную матрицу в качестве входных данных и реверсирование строк входных данных).матрица):

matrix = [[5, -2, -1, 5, 3, -99, 4, 0],
[5, -2, -1, -3, 3, -99, 2, -3],
[-98, -98, -98, -98, -98, -98, -98, -98],
[-99, -99, -99, -99, -99, -99, -99, -99],
[5, 0, -3, 5, -1, 7, -2, 2],
[1, 2, 7, 0, 0, 1, -1, -1]]

H = len(matrix)
W = len(matrix[0])

for row in matrix:
    row.reverse()

h = [[[0] * (W + 1) for i in range(W)] for j in range(H)]

h[0][0][0] = matrix[0][0]
h[0][0][W] = h[0][0][0]

for x in range(1, W):
    h[0][x][x] = h[0][x-1][x-1] + matrix[0][x] - x
    h[0][x][W] = h[0][x][x]

for y in range(1, H):
    h[y][0][0] = matrix[y][0] + max(h[i][0][0] for i in range(y))
    h[y][0][W] = h[y][0][0]

for y in range(1, H):
    for x in range(1, W):
        h[y][x][0] = matrix[y][x] + max(h[j][x][W] for j in range(y))
        h[y][x][W] = h[y][x][0]
        for r in range(1, x + 1):
            h[y][x][r] = h[y][x-1][r-1] + matrix[y][x] - r
            h[y][x][W] = max(h[y][x][W], h[y][x][r])

print(h[H-1][W-1][W])
...