Редакция для Острова пепельного моря
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
1. Идея
Нужно найти все острова на карте и для каждого посчитать его площадь, то есть количество клеток со значением 1, которые связаны по сторонам.
Это классическая задача на обход компонент связности в сетке. Если встретили клетку суши 1, которая ещё не была посещена, значит, начинается новый остров. Запускаем из неё обход в глубину или в ширину, отмечаем все достижимые клетки этого острова и считаем их количество. После этого обновляем ответ — максимум из всех найденных площадей.
В эталонном решении используется итеративный DFS через стек.
2. Наблюдения
- Остров образуют только клетки
1. - Связь есть только по четырём направлениям:
- вверх
- вниз
- влево
- вправо
- Если клетка уже была посещена при обходе какого-то острова, повторно её рассматривать не нужно.
- Каждая клетка карты попадёт в обход не более одного раза, поэтому решение будет достаточно быстрым.
- Если на всей карте нет ни одной
1, то ответ останется равным0.
3. Алгоритм
Пусть размеры карты — n строк и m столбцов.
- Считываем таблицу
grid. - Создаём массив
usedтакого же размера, где будем хранить, посещали ли клетку. - Заводим переменную
ans = 0. - Проходим по всем клеткам
(i, j):- если
grid[i][j] != 1, пропускаем; - если
used[i][j] = true, тоже пропускаем; - иначе нашли новую компоненту связности, то есть новый остров.
- если
- Для нового острова:
- создаём стек;
- кладём туда клетку
(i, j); - помечаем её посещённой;
- запускаем цикл, пока стек не пуст:
- достаём клетку;
- увеличиваем текущую площадь
area; - смотрим 4 соседей;
- если сосед внутри границ, не посещён и равен
1, помечаем его и кладём в стек.
- После завершения обхода этого острова обновляем
ans = max(ans, area). - После обработки всей таблицы выводим
ans.
4. Почему это работает
Рассмотрим любую клетку суши.
- Если она ещё не посещена, то из неё начинается обход, который пойдёт только по клеткам
1, связанным по стороне. - Значит, будут найдены все и только те клетки, которые принадлежат одному острову.
- Каждая такая клетка добавится в стек ровно один раз, потому что перед добавлением мы сразу отмечаем её как посещённую.
- Следовательно, значение
areaпосле завершения обхода равно площади ровно этого острова.
Теперь посмотрим на всю карту.
- Внешний двойной цикл перебирает все клетки.
- Для каждого острова обязательно найдётся первая непосещённая клетка, из которой мы запустим обход.
- После этого весь остров будет отмечен, и повторно он уже не обработается.
- Значит, каждый остров будет посчитан ровно один раз.
Мы берём максимум из площадей всех найденных островов, поэтому итоговый ans и есть площадь наибольшего острова.
5. Сложность
Пусть n — число строк, m — число столбцов.
- Время:
O(n * m), потому что каждая клетка рассматривается ограниченное число раз. - Память:
O(n * m)на массив посещения и стек в худшем случае.
При ограничениях до 50 x 50 это работает мгновенно.
6. Код на C++17
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> grid[i][j];
}
}
vector<vector<char>> used(n, vector<char>(m, 0));
int ans = 0;
const int dx[4] = {1, -1, 0, 0};
const int dy[4] = {0, 0, 1, -1};
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (grid[i][j] != 1 || used[i][j]) continue;
int area = 0;
stack<pair<int, int>> st;
st.push({i, j});
used[i][j] = 1;
while (!st.empty()) {
auto [x, y] = st.top();
st.pop();
++area;
for (int dir = 0; dir < 4; ++dir) {
int nx = x + dx[dir];
int ny = y + dy[dir];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (used[nx][ny] || grid[nx][ny] != 1) continue;
used[nx][ny] = 1;
st.push({nx, ny});
}
}
ans = max(ans, area);
}
}
cout << ans << '\n';
return 0;
}
7. Код на Python 3
import sys
def main():
data = sys.stdin.buffer.read().split()
if not data:
return
it = iter(data)
n = int(next(it))
m = int(next(it))
grid = [[0] * m for _ in range(n)]
for i in range(n):
for j in range(m):
grid[i][j] = int(next(it))
used = [[False] * m for _ in range(n)]
ans = 0
dirs = [(1, 0), (-1, 0), (0, 1), (0, -1)]
for i in range(n):
for j in range(m):
if grid[i][j] != 1 or used[i][j]:
continue
area = 0
stack = [(i, j)]
used[i][j] = True
while stack:
x, y = stack.pop()
area += 1
for dx, dy in dirs:
nx = x + dx
ny = y + dy
if 0 <= nx < n and 0 <= ny < m and not used[nx][ny] and grid[nx][ny] == 1:
used[nx][ny] = True
stack.append((nx, ny))
if area > ans:
ans = area
print(ans)
if __name__ == "__main__":
main()
Комментарии