Задание №18 — Задачи на динамику для таблиц
Квадрат разлинован на N × N клеток (1 < N < 30). Исполнитель Робот может перемещаться по клеткам, выполняя за одно перемещение одну из двух команд: вправо или вниз. По команде вправо Робот перемещается в соседнюю правую клетку, по команде вниз – в соседнюю нижнюю. Квадрат ограничен внешними стенами. Между соседними клетками квадрата также могут быть внутренние стены. Сквозь стену Робот пройти не может.
Перед каждым запуском Робота в каждой клетке квадрата лежит монета достоинством от 1 до 100. Посетив клетку, Робот забирает монету с собой; это также относится к начальной и конечной клеткам маршрута Робота.
В «угловых» клетках поля – тех, которые справа и снизу ограничены стенами, Робот не может продолжать движение, поэтому накопленная сумма считается итоговой. Таких конечных клеток на поле может быть несколько, включая правую нижнюю клетку поля. При разных запусках итоговые накопленные суммы могут различаться.
Определите максимальную и минимальную денежные суммы, среди всех возможных итоговых сумм, которые может собрать Робот, пройдя из левой верхней клетки в конечную клетку маршрута. В ответе укажите два числа – сначала максимальную сумму, затем минимальную.
Исходные данные представляют собой электронную таблицу размером N × N, каждая ячейка которой соответствует клетке квадрата. Внутренние и внешние стены обозначены утолщёнными линиями.
Пример входных данных:
| 1 | 8 | 8 | 4 |
| 10 | 1 | 1 | 3 |
| 1 | 3 | 12 | 2 |
| 2 | 3 | 5 | 6 |
Правильный ответ
2167 718
Пояснение
Решение:
Робот ходит только вправо и вниз, поэтому в любую клетку он может прийти лишь слева или сверху. Это классическая динамика по таблице: рядом с исходным полем строим ещё две таблицы того же размера — в первой накапливаем максимально возможную сумму, во второй минимально возможную.
В левую верхнюю клетку обеих таблиц записываем саму монету (в этом файле это 99). Остальные клетки заполняем построчно слева направо по правилу: значение равно монете из исходной таблицы плюс наибольшее (соответственно наименьшее) из значений соседей слева и сверху, причём сосед учитывается только тогда, когда между ним и текущей клеткой нет утолщённой линии — стены. Если оба входа перекрыты стенами, клетка недостижима и остаётся пустой. Например, если исходные данные лежат в диапазоне A1:T20, а таблицу максимумов разместить в A23:T42, то в клетку B24 (она отвечает клетке R2C2 поля) пишем =B2+МАКС(A24;B23), а там, где вход перекрыт стеной, соответствующий аргумент из формулы просто убираем.
Маршрут заканчивается в «угловой» клетке — той, у которой перекрыты и правый, и нижний переходы. В этом файле таких достижимых клеток три: R15C18, R20C12 и R20C20. Ответ ищем только среди них, а не по всей таблице. Наибольшая накопленная сумма получается в правой нижней клетке R20C20 и равна 2167, наименьшая — в клетке R15C18 и равна 718.
import openpyxl
ws = openpyxl.load_workbook('18.xlsx').active
N = 20
coin = [[ws.cell(r + 1, c + 1).value for c in range(N)] for r in range(N)]
def thick(r, c, side): # утолщённая линия = стена
return getattr(ws.cell(r + 1, c + 1).border, side).style == 'thick'
def wall_right(r, c):
return c == N - 1 or thick(r, c, 'right') or thick(r, c + 1, 'left')
def wall_down(r, c):
return r == N - 1 or thick(r, c, 'bottom') or thick(r + 1, c, 'top')
hi = [[None] * N for _ in range(N)]
lo = [[None] * N for _ in range(N)]
hi[0][0] = lo[0][0] = coin[0][0]
for r in range(N):
for c in range(N):
if hi[r][c] is None:
continue
for nr, nc, blocked in ((r, c + 1, wall_right(r, c)),
(r + 1, c, wall_down(r, c))):
if blocked:
continue
a = hi[r][c] + coin[nr][nc]
b = lo[r][c] + coin[nr][nc]
hi[nr][nc] = a if hi[nr][nc] is None else max(hi[nr][nc], a)
lo[nr][nc] = b if lo[nr][nc] is None else min(lo[nr][nc], b)
ends = [(r, c) for r in range(N) for c in range(N)
if hi[r][c] is not None and wall_right(r, c) and wall_down(r, c)]
print(max(hi[r][c] for r, c in ends), min(lo[r][c] for r, c in ends))
# 2167 718
Ответ: 2167 718