이 문제는 2차원 배열에서 상, 하, 좌, 우로 연결된 부분을 하나의 덩어리로 간주하여, 0으로 이루어진 덩어리(즉, 아이스크림 개수)를 구하는 문제이다.
DFS/BFS 문제를 처음 접해봤기 때문에 어떻게 접근해야 할지 감이 오지 않았다. 일단 DFS 문제라는 것만 알고, DFS 동작 과정을 다시 한번 정독한 다음 문제를 풀어봤다. (참고로 BFS로도 풀 수 있다고 한다.)
# DFS
n, m = map(int, input().split())
a = [list(input().strip()) for _ in range(n)]
# 상, 하, 좌, 우
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
cnt = 0
def dfs(i, j):
d = []
for k in range(4):
x = i + dx[k]
y = j + dy[k]
if 0 <= x < n and 0 <= y < m and a[x][y] == '0':
d.append([x, y])
if d:
for l in d:
a[l[0]][l[1]] = '1'
dfs(l[0], l[1])
for i in range(n):
for j in range(m):
if a[i][j] == '0':
a[i][j] = '1'
cnt += 1
dfs(i, j)
print(cnt)
dx, dy 리스트를 사용해 상하좌우 방향을 탐색하도록 구현했다.d 리스트에 추가한 후, 해당 노드들에 대해 재귀적으로 dfs를 호출했다.cnt는 처음 0을 발견할 때만 증가시키도록 했다. (하나의 덩어리를 찾을 때마다 1 증가)책에서 제시한 정답 코드는 다음과 같다.
n, m = map(int, input().split())
# 2차원 리스트의 맵 정보 입력받기
graph = []
for i in range(n):
graph.append(list(map(int, input())))
# DFS로 특정한 노드를 방문한 뒤에 연결된 모든 노드들도 방문
def dfs(x, y):
# 주어진 범위를 벗어나는 경우에는 즉시 종료
if x <= -1 or x >= n or y <= -1 or y >= m:
return False
# 현재 노드를 아직 방문하지 않았다면
if graph[x][y] == 0:
# 해당 노드 방문 처리
graph[x][y] = 1
# 상, 하, 좌, 우의 위치도 모두 재귀적으로 호출
dfs(x-1, y)
dfs(x, y-1)
dfs(x+1, y)
dfs(x, y+1)
return True
return False
# 모든 노드(위치)에 대하여 음료수 채우기
result = 0
for i in range(n):
for j in range(m):
# 현재 위치에서 DFS 수행
if dfs(i, j) == True:
result += 1
print(result)
초기 DFS 호출 방식
dfs 함수 내부에서 d 리스트를 만들어 방문할 노드를 먼저 모은 후 재귀적으로 탐색했다.dfs를 호출하고, 유효한 좌표인지 여부를 dfs 함수의 처음 부분에서 처리했다.방문 처리 방식
d 리스트를 사용해서 먼저 방문할 노드를 따로 저장한 후 하나씩 방문 처리했다.dfs가 호출되었을 때 바로 graph[x][y] = 1로 방문 처리를 했다.결과 값 증가 방식
cnt를 사용하여 0을 처음 만날 때마다 1씩 증가시켰다.dfs 함수가 True를 반환하는 경우에만 result += 1을 했다. 즉, dfs가 한 덩어리를 모두 탐색한 후 True를 반환하면 한 개의 아이스크림을 찾았다고 판단했다.DFS 호출 구조
dfs를 호출하고 함수 내부에서 유효성을 검사하는 방식이 더 직관적이고 간결하다.결과 값 증가 위치
dfs 함수가 True를 반환하는 시점에서 result += 1을 하면 더 깔끔하게 덩어리를 세는 로직을 구현할 수 있다.방문 처리 순서
dfs를 호출하자마자 방문 처리를 하는 것이 중복 방문을 방지하는 데 효과적이다.다음은 정답 코드를 기반으로 하여 BFS로 구현해본 코드이다.
from collections import deque
n, m = map(int, input().split())
# 2차원 리스트의 맵 정보 입력받기
graph = []
for i in range(n):
graph.append(list(map(int, input())))
# 상, 하, 좌, 우
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
# BFS로 특정한 노드를 방문한 뒤에 연결된 모든 노드들도 방문
def bfs(x, y):
q = deque([(x, y)])
graph[x][y] = 1
while q:
vx, vy = q.popleft()
for k in range(4):
nx = vx + dx[k]
ny = vy + dy[k]
if 0 <= nx < n and 0 <= ny < m and graph[nx][ny] == 0:
q.append((nx, ny))
graph[nx][ny] = 1
return True
# 모든 노드(위치)에 대하여 음료수 채우기
result = 0
for i in range(n):
for j in range(m):
# 현재 위치에서 BFS 수행
if graph[i][j] == 0 and bfs(i, j) == True:
result += 1
print(result)
이 문제에서는 최단 거리를 구해야 하기 때문에 BFS가 유리하다.
하지만 책의 힌트를 얻고 보니 핵심 아이디어는 '새롭게 방문하는 노드'의 값을 1씩 증가시키는 것이었다.
from collections import deque
n, m = map(int, input().split())
graph = []
for _ in range(n):
graph.append(list(map(int, input())))
# 상, 하, 좌, 우
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
def bfs(x, y):
q = deque([(x, y)])
while q:
vx, vy = q.popleft()
for k in range(4):
nx = vx + dx[k]
ny = vy + dy[k]
if 0 <= nx < n and 0 <= ny < m and graph[nx][ny] == 1:
q.append((nx, ny))
graph[nx][ny] = graph[vx][vy] + 1
return graph[n-1][m-1]
print(bfs(0, 0))
📌 내가 배운 점