Источник задачи: региональный этап Олимпиады им. М.В. Келдыша, 2025, задача C.
Что дано
Прямоугольное поле размера клеток. Каждая клетка содержит один из трёх символов: . (пустая клетка), # (камень) или g (золотой слиток). Параметр задаёт радиус взрыва.
Взрыв можно выполнить в любой пустой клетке внутри поля. При взрыве действует квадрат со стороной , центрированный на . Механика взрыва:
- Все клетки квадрата становятся пустыми (камни и слитки исчезают).
- Слитки строго внутри квадрата (в подквадрате , не на границе) — теряются.
- Слитки на периметре квадрата (внешний слой шириной в одну клетку) — собираются.
Квадрат может выходить за пределы поля — клетки за границей поля ни на что не влияют. Разрешено выполнять произвольное число взрывов. Нужно найти максимум слитков, которые можно собрать.
Ограничения
- Хотя бы одна клетка поля гарантированно пуста (иначе первый взрыв невозможен).
Формат ввода
Первая строка — три числа , , через пробел. Далее — строк по символов каждая, описывающих поле построчно.
Формат вывода
Одно целое число — максимальное количество собранных слитков.
Примеры
Пример 1. Поле , :
2 3 1
#.#
g.g
Ожидаемый ответ: 2.
Пример 3. Поле , :
3 4 2
.gg.
g...
g##.
Ожидаемый ответ: 4 (все слитки поля).
Пример 1 руками
m=2, n=3, k=1
#.#
g.g
Квадрат взрыва со стороной . Центр — любая из пустых клеток или . Квадрат с центром займёт строки и столбцы .
«Строго внутри» — это строки и столбцы , то есть одна клетка — сам центр, который пустой. Потерь нет.
«На периметре» — 8 клеток по границе квадрата. В пределах поля: . Из них золото на и — собираем 2 слитка.
Ответ: 2. ✓
Пример 3 руками
m=3, n=4, k=2
.gg.
g...
g##.
Всего золото: — 4 слитка.
Квадрат взрыва (). «Строго внутри» — квадрат с тем же центром.
Попробуем центр (нижний-правый, пустой). Внутренность — строки и столбцы . В пределах поля: строки , столбцы . Клетки: — золота внутри нет.
Значит при этом первом взрыве мы теряем 0, собираем то, что на периметре в пределах поля — проверим: периметр квадрата это строки и столбцы . В поле попадает строка 0 (вся → из-за колонок) и столбец (клетки ). Золото: , — собрали 2 слитка.
Остаются . Нужен второй взрыв. После первого взрыва квадрат из строк и столбцов уже пустой. Делаем второй взрыв в ? Но , занят. Возьмём — пустая. Квадрат с центром там же, сторона 5, но ключ в том, что мы можем расширить: взрывать на «новом» периметре (квадрат со стороной 7 с тем же центром )? Нет — нельзя, сам взрыв всегда фиксированного размера .
Правильно: делаем следующий взрыв так, чтобы новый квадрат захватил ранее непокрытые клетки. Центр нового взрыва смещаем на клетку — например, . Но , занята. А клетки строки 1 и 2, в столбце 0 — с золотом, с золотом. Хочется центр в (пустая), квадрат строк , столбцов . В поле — всё поле целиком (строки , столбцы ). Всё это становится пустым, и золото и — на периметре квадрата со стороной 5 с центром ? Периметр — строки и , столбцы и . Нет, они в столбце 0 и строках 1, 2 — это строго внутри квадрата.
Значит прямой второй взрыв терять будет. Похоже, нужно выбрать другой центр первого взрыва.
Важное наблюдение (оно и есть идея): после первого взрыва можно «перешагивать» взрывами, у которых предыдущий квадрат взрыва становится частью центра — и через несколько шагов дойти до любой оставшейся клетки. Но это работает, только если при каждом следующем взрыве центр находится в уже опустошённой области (пустая клетка), и новый квадрат не содержит потерянного золота во внутренности.
Ключ: если первый взрыв уже потерял какое-то золото, остальное потом собирается целиком — потому что следующие взрывы можно располагать так, чтобы золото всегда оказывалось на периметре.
Об этом ниже.
Идея решения
Ключевое наблюдение: «потери только на первом взрыве»
Рассмотрим первый взрыв. Всё золото строго внутри его квадрата ( со стороной) теряется — это фиксированная плата за «начало работы». Но всё остальное золото на поле мы потом соберём гарантированно: достаточно расположить следующие взрывы так, чтобы их центры оказывались на периметре уже расширяемой пустой зоны, а новое золото попадало только на периметры новых квадратов (откуда оно собирается, а не теряется).
Значит задача сводится к минимизации золота внутренности первого квадрата: перебрать все допустимые центры первого взрыва (пустые клетки поля) и выбрать тот, при котором потеря минимальна. Сумма на произвольном квадрате считается за через 2D-префиксные суммы по позициям золота — значит общая сложность .
Пусть первый взрыв в . Внутри квадрата со стороной — всё золото строго во внутреннем квадрате пропало. Золото на периметре — собрано.
Теперь нужно дойти до остальных клеток. Алгоритм: берём границу следующего квадрата (стороны ) с тем же центром — клетки этой границы пока не опустошены (они за пределами первого взрыва). Можно делать новые взрывы, центрируя их на этой границе. Но центр взрыва должен быть в пустой клетке — для этого границы идут изнутри наружу: на границе квадрата есть клетки, которые на периметре первого взрыва (т.е. были на границе ) — они уже пустые. Ставим взрыв центром туда; новый квадрат захватывает ранее непокрытую полосу, и всё золото на её периметре — собираем.
Повторяя, расширяем покрытие квадратами , , , …
Важно: при втором и дальнейших взрывах никакое золото не теряется, потому что:
- Всё, что окажется «внутри» нового квадрата, — это уже ранее опустошённая область (там золота нет).
- Новое золото встречается только на новой границе (которая становится периметром нового взрыва) — значит собирается.
Это геометрически несложно проверить, но требует аккуратной индукции.
Вывод. Пусть — суммарное количество золота на поле. Минимизируем — количество золота строго внутри квадрата первого взрыва с центром . Ответ:
Что такое «строго внутри»?
Квадрат со стороной с центром покрывает строки и столбцы .
Периметр — первая и последняя строка, первый и последний столбец: , , , .
«Строго внутри» — подквадрат строк и столбцов , то есть размером . При это одна клетка — сам центр, который пустой по условию, так что . Тогда ответ всегда, если есть хоть одна пустая клетка.
Как быстро считать ?
Префиксные суммы по золоту:
Тогда сумма в прямоугольнике (включительно):
С учётом клиппинга границ поля . на запрос, на построение.
Перебираем все пустых клеток — всего .
Решение: псевдокод
читать m, n, k, сетку
P = 2D-префиксная сумма по g-клеткам
G = P[m][n] # всё золото
best_lost = G # хуже не бывает
нашли_пустую = false
для каждой (r, c) в сетке:
если grid[r][c] != '.': continue
нашли_пустую = true
r1 = max(0, r - k + 1), r2 = min(m - 1, r + k - 1)
c1 = max(0, c - k + 1), c2 = min(n - 1, c + k - 1)
если r1 > r2 или c1 > c2:
lost = 0
иначе:
lost = P[r2+1][c2+1] - P[r1][c2+1] - P[r2+1][c1] + P[r1][c1]
best_lost = min(best_lost, lost)
если не нашли_пустую:
вывести 0
иначе:
вывести G - best_lost
Код решения
Проверим на всех примерах
Пример 1
2 3 1
#.#
g.g
(золото на и ).
, внутренность — 1 клетка. Пустые: и . В обеих случаях внутренность — сама эта клетка, золота нет. для обеих.
Ответ: ✓
Пример 2
2 3 2
#.#
g.g
. , внутренность .
- : внутренность строки , столбцы . Клиппинг: строки , столбцы . Золото внутри: → .
- : внутренность строки , столбцы . Клиппинг: строки , столбцы . Золото: то же 2. .
. Ответ: ✓
Пример 3
3 4 2
.gg.
g...
g##.
(золото на ).
, внутренность .
Пустые клетки (не # и не g): .
Для каждой считаем на :
- : строки → , столбцы → . Золото: . .
- : строки → , столбцы → . Золото: . .
- : строки , столбцы . Золото: → .
- : строки , столбцы . Золото: → .
- : строки , столбцы → . Золото: → .
- : строки → , столбцы → . Золото: нет (в этом прямоугольнике). .
(клетка ). Ответ: ✓
Крайние случаи
1. Нет золота
. Ответ: 0, независимо от центра.
2.
Внутренность — одна клетка (сам центр). Центр пустой по условию → всегда. Ответ: (всё золото собирается).
3. Квадрат выходит за пределы поля
Клиппинг r1..r2, c1..c2 по границам . Если пусто (r1 > r2) — .
Это полезно: при взрыве в углу большая часть квадрата — за полем, потерь меньше. Алгоритм учитывает это автоматически.
4. Единственная пустая клетка
Перебор даст ровно один — он же и ответ.
5. Поле целиком из камней # (кроме одной пустой клетки — гарантируется)
→ ответ .
6. Максимальные
Поле клеток, префсумма памяти и времени. Перебор — . Никаких проблем ни по времени, ни по памяти.
Типичные ошибки
-
Путаница «внутри» vs «на периметре» vs «весь квадрат». Внимательно: внутренность — это квадрат , не .
-
Размер внутренности при . Одна клетка, не пусто. Не путать с .
-
Неучёт клиппинга. При взрыве у границы поля не все клетки внутренности существуют. Без клиппинга получишь выход за массив или неправильный счёт.
-
Перебирать не только пустые. Центр обязан быть пустым. Если перебираешь все клетки без фильтра — получаешь неверные для
#иg-клеток (да и брать центром золотой слиток, который исчезнет, — это теряет сам слиток). -
Забыть про случай
found_empty == false. В условии гарантируется хотя бы одна пустая клетка, но хорошо иметь этот защитный путь. -
Префсумма off-by-one. Классическая ошибка — путать индексы и . Формула:
Сложность
- Время: (построение префсуммы + перебор пустых клеток с запросами).
- Память: (префсумма).
Что ещё полезно потренировать
Задачи на 2D-префиксные суммы + жадный или конструктивный выбор «лучшей стартовой точки»:
- Codeforces 835B «The number on the board» — жадность по цифрам с сортировкой.
- Codeforces 1301B «Disturbed Letters» / 1175A «From Hero to Zero» — перебор с проверкой.
- Codeforces 1114D «Flood Fill» — DP после интуитивной редукции.
- acmp.ru задача 501 «Магический квадрат» — префсумма 2D + перебор.
Общий мотив: заметить, что ответ — это «что-то от единственного выбора», и свести перебор к или с помощью префсуммы.
Итого
- Идея: ответ = всё золото минус минимальная потеря на внутренности квадрата первого взрыва.
- Почему: после первого взрыва оставшееся золото собирается целиком — расширяя покрытие концентрическими квадратами стороны
- Инструмент: 2D-префсумма для запроса суммы в прямоугольнике, перебор пустых клеток.
- Сложность: .
- Ловушки: внутренность — , не ; клиппинг границ поля; перебирать только пустые клетки.