N×M의 모눈종이 위에 아주 얇은 치즈가 <그림 1>과 같이 표시되어 있다. 단, N 은 세로 격자의 수이고, M 은 가로 격자의 수이다. 이 치즈는 냉동 보관을 해야만 하는데 실내온도에 내어놓으면 공기와 접촉하여 천천히 녹는다. 그런데 이러한 모눈종이 모양의 치즈에서 각 치즈 격자(작 은 정사각형 모양)의 4변 중에서 적어도 2변 이상이 실내온도의 공기와 접촉한 것은 정확히 한시간만에 녹아 없어져 버린다. 따라서 아래 <그림 1> 모양과 같은 치즈(회색으로 표시된 부분)라면 C로 표시된 모든 치즈 격자는 한 시간 후에 사라진다.
<그림 1>
<그림 2>와 같이 치즈 내부에 있는 공간은 치즈 외부 공기와 접촉하지 않는 것으로 가정한다. 그러므 로 이 공간에 접촉한 치즈 격자는 녹지 않고 C로 표시된 치즈 격자만 사라진다. 그러나 한 시간 후, 이 공간으로 외부공기가 유입되면 <그림 3>에서와 같이 C로 표시된 치즈 격자들이 사라지게 된다.
<그림 2>
<그림 3> 모눈종이의 맨 가장자리에는 치즈가 놓이지 않는 것으로 가정한다. 입력으로 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 구하는 프로그램을 작성하시오.
첫째 줄에는 모눈종이의 크기를 나타내는 두 개의 정수 N, M (5 ≤ N, M ≤ 100)이 주어진다. 그 다음 N개의 줄에는 모눈종이 위의 격자에 치즈가 있는 부분은 1로 표시되고, 치즈가 없는 부분은 0으로 표시된다. 또한, 각 0과 1은 하나의 공백으로 분리되어 있다.
출력으로는 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 정수로 첫 줄에 출력한다.
이 문제는 문제를 구현해주면 해결할 수 있는 문제입니다. 가장 쉽게 생각할 수 있는 로직은 맞닿은 공기가 외부인지 확인하고 이러한 외부 공기가 2개이면 치즈를 녹이는 방식을 이용할 수 있습니다. 하지만, 이렇게 코드를 작성하게 되는 경우 치즈의 맞닿은 공기가 외부인지 확인하는 과정을 계속해서 반복하게 됩니다. 그렇게 되면 시간복잡도가 좋지 못하겠죠.
그래서 외부의 공기를 먼저 표시하고 외부 공기와 2개 이상 맞닿은 치즈를 녹이는 방법으로 풀이를 진행하겠습니다.
기본적으로 외부와 맞닿은 공기들은 가장 바깥쪽에 있습니다. 그러므로 인덱스를 초과하는 부분의 공기들로부터 맞닿은 모든 공기는 외부의 공기입니다. 이렇게 공기를 표시한 후, 치즈가 외부 공기와 2개 이상 맞닿아 있다면 치즈를 녹입니다. 그렇게 치즈를 점점 녹이면서 치즈가 전부 사라질 경우 동작을 종료하고 정답을 출력하면 됩니다.
import sys
from collections import deque
input = sys.stdin.readline
# 방향벡터
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
# 외부와 맞닿은 공기인지 확인하는 함수
def check(x, y):
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if nx < 0 or nx >= n or ny < 0 or ny >= m:
return True
return False
# 외부와 맞닿은 공기와 맞닿은 공기 모두 외부 공기로 표시하는 BFS
def bfs(x, y):
visited = [[False for _ in range(m)] for _ in range(n)]
q = deque()
ex.append((x, y))
visited[x][y] = True
graph[x][y] = 2
q.append((x, y))
while q:
x, y = q.popleft()
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
if not visited[nx][ny] and graph[nx][ny] == 0:
visited[nx][ny] = True
graph[nx][ny] = 2
q.append((nx, ny))
ex.append((nx, ny))
n, m = map(int, input().split())
graph = [list(map(int, input().split())) for _ in range(n)]
result = 0
while True:
# 계속 수행하는지 확인하는 불리언 변수
flag = False
for x in range(n):
for y in range(m):
# 외부와 맞닿은 공기는 모두 표시
if graph[x][y] == 0 and check(x, y):
bfs(x, y)
# 치즈가 존재하면 반복문 계속 수행 명시
if graph[x][y] == 1:
flag = True
# 치즈가 없는 경우 중지
if not flag:
break
for x in range(n):
for y in range(m):
if graph[x][y] == 1:
cnt = 0
외부와 맞닿은 공기가 2개 이상이면 녹임
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if graph[nx][ny] == 2:
cnt += 1
if cnt == 2:
graph[x][y] = 0
break
# 표시된 외부 공기는 다시 0으로 표시
for x, y in ex:
graph[x][y] = 0
# 계속 진행되는 경우 시간이 흘러감
result += 1
print(result)
문제에서는 CHECK 함수를 만들어서 외부와 맞닿았는지 확인하도록 하였습니다. 그리고 외부와 맞닿은 공기와 맞닿은 모든 공기를 2로 표시하여 후에 치즈가 녹는지 확인할 것입니다. 그리고 임의로 2로 변경된 모든 공기들은 다시 0으로 초기화해서 새롭게 수행을 해야 정확하게 다음 치즈들을 녹일 수 있습니다.

저는 처음에는 치즈와 맞닿은 공기가 외부 공기인지 확인하도록 로직을 작성하였는데요. 시간복잡도가 너무 안좋아서 풀이를 변경하게 되었습니다. 변경 전 풀이도 보여드리겠습니다.
import sys
from collections import deque
input = sys.stdin.readline
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
# 외부와 맞닿은 공기인지 확인하는 BFS
def bfs(x, y):
visited = [[False for _ in range(m)] for _ in range(n)]
q = deque()
q.append((x, y))
visited[x][y] = True
while q:
x, y = q.popleft()
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if nx < 0 or nx >= n or ny < 0 or ny >= m:
return True
if not visited[nx][ny] and graph[nx][ny] == 0:
visited[nx][ny] = True
q.append((nx, ny))
return False
n, m = map(int, input().split())
graph = [list(map(int, input().split())) for _ in range(n)]
result = 0
while True:
# 계속 수행하는지 확인하는 불리언 변수
flag = False
# 녹는 치즈를 저장하는 배열
tmp = []
for x in range(n):
for y in range(m):
if graph[x][y] == 1:
flag = True
cnt = 0
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
# 외부와 맞닿은 치즈인 경우 cnt 1 증가
if graph[nx][ny] == 0 and bfs(nx, ny):
cnt += 1
if cnt >= 2:
# 이 치즈는 녹을 예정
tmp.append((x, y))
break
# 치즈를 녹임
for x, y in tmp:
graph[x][y] = 0
if not flag:
break
else:
result += 1
print(result)
결과적으로 2.2초에서 0.3초로 수행시간을 줄일 수 있었고, 메모리도 줄일 수 있었습니다.

