Задание №18 — Задачи на динамику для таблиц
Квадрат разлинован на N × N клеток (1 < N < 20). Исполнитель Робот может перемещаться по клеткам, выполняя за одно перемещение одну из двух команд: вправо или вниз. По команде вправо Робот перемещается в соседнюю правую клетку, по команде вниз — в соседнюю нижнюю. Квадрат ограничен внешними стенками. Между соседними клетками квадрата также могут быть внутренние стены. Сквозь стену Робот пройти не может. Перед каждым запуском Робота в каждой клетке квадрата лежит монета достоинством от 1 до 100. Посетив клетку, Робот забирает с собой монету; это также относится к начальной и конечной клеткам маршрута Робота.
Определите минимальную и максимальную денежные суммы, которые может собрать Робот, пройдя из левой верхней клетки в правую нижнюю. В ответе укажите два числа — сначала минимальную сумму, затем максимальную. Исходные данные представляют собой электронную таблицу размером N × N, каждая ячейка которой соответствует клетке квадрата.
Пример входных данных:
| 1 | 8 | 8 | 4 |
| 10 | 1 | 1 | 3 |
| 1 | 3 | 12 | 2 |
| 2 | 3 | 5 | 6 |
Правильный ответ
508 731
Пояснение
Решение:
Робот ходит только вправо и вниз, поэтому в любую клетку он приходит либо сверху, либо слева и ни одной клетки не проходит дважды. Значит, работает динамическое программирование: для каждой клетки достаточно хранить максимальную и минимальную суммы, которые можно накопить, дойдя до неё из левой верхней клетки.
Пусть — номинал монеты в клетке, и — максимальная и минимальная накопленные суммы. Тогда и , причём соседняя клетка учитывается только тогда, когда между ней и текущей нет стены. Для левой верхней клетки .
В приложенном файле поле имеет размер . Единственная внутренняя стена (утолщённая линия) — горизонтальная, между 6-й и 7-й строками в столбцах с 5-го по 10-й: в эти клетки 7-й строки нельзя попасть сверху, только слева.
Скопируем содержимое таблицы в текстовый файл 18.txt (числа разделены пробелами или знаками табуляции) и обработаем его программой.
f = open('18.txt')
a = [[int(x) for x in s.split()] for s in f]
N = len(a)
# стена снизу от клетки (i, j); строки и столбцы нумеруются с нуля
def wall_down(i, j):
return i == 5 and 4 <= j <= 9
# стена справа от клетки (i, j) — вертикальных внутренних стен нет
def wall_right(i, j):
return False
MX = [[0] * N for i in range(N)] # максимум, накопленный к клетке
MN = [[0] * N for i in range(N)] # минимум
for i in range(N):
for j in range(N):
if i == 0 and j == 0:
MX[i][j] = MN[i][j] = a[0][0]
continue
v = []
if i > 0 and not wall_down(i - 1, j):
v.append((MX[i - 1][j], MN[i - 1][j]))
if j > 0 and not wall_right(i, j - 1):
v.append((MX[i][j - 1], MN[i][j - 1]))
MX[i][j] = max(x for x, y in v) + a[i][j]
MN[i][j] = min(y for x, y in v) + a[i][j]
print(MN[N - 1][N - 1], MX[N - 1][N - 1])
Программа выводит 508 731: минимальная сумма равна 508, максимальная — 731. В ответе сначала записывается минимальная сумма, затем максимальная.
Ответ: 508 731