백준 21736번 - 헌내기는 친구가 필요해

헌내기는 친구가 필요해 문제 바로 가기

풀이 날짜: 2026-01-18

baekjoon 알고리즘 문제 풀이

풀이 코드


import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**6)

n,m = map(int, input().split())
graph = [list(input().strip()) for _ in range(n)]
visited = [[False]*m for _ in range(n)]

dx = [0,0,-1,1]
dy = [-1,1,0,0]

def dfs(x,y):
    global count
    stack = [(x,y)]
    visited[x][y] = True

    while stack:
        x,y = stack.pop()
        if graph[x][y] == 'P':
            count += 1

        for i in range(4):
            nx = dx[i] + x
            ny = dy[i] + y
            if 0<=nx<n and 0<=ny<m and not visited[nx][ny]:
                if graph[nx][ny] != 'X':
                    visited[nx][ny] = True
                    stack.append((nx,ny))

count = 0

found = False
for i in range(n):
    for j in range(m):
        if graph[i][j] == 'I':
            dfs(i,j)
            found = True
            break
    if found:
        break

if count == 0:
    print('TT')
else:
    print(count)
  

문제 접근

이 문제는 n x m 격자에서 시작 위치 I에서 출발해 상/하/좌/우로 이동하면서, 벽 X는 통과하지 못하고, 이동 가능한 모든 칸을 탐색한 뒤 만나는 사람 P의 수를 세는 문제다. 탐색을 끝냈을 때 P를 하나도 못 만나면 “TT”, 한 명이라도 만나면 그 숫자를 출력한다. 결국 “그래프 탐색(DFS/BFS)으로 연결된 영역을 전부 돌면서 조건에 맞는 칸을 카운트”하는 전형적인 문제다.


visited가 필요한 이유 (무한 루프 방지)

격자 탐색은 한 칸에서 네 방향으로 이동이 가능하므로, 방문 체크가 없으면 같은 칸을 계속 왕복하면서 무한 반복에 빠진다. 이를 막기 위해 visited[n][m]를 두고, 어떤 칸을 스택/큐에 넣는 순간(혹은 꺼내서 처리하는 순간) 방문 처리한다. 이 문제에서는 “한 번 방문한 칸은 다시 볼 필요가 없다”가 성립하므로, visited는 시간복잡도를 O(n*m)으로 제한하는 데 핵심 역할을 한다.


“I는 하나라서 찾으면 break” 최적화의 의미

전체 격자를 돌며 시작점 ‘I’를 찾는데, 일반적으로 ‘I’는 하나만 존재한다. 그래서 ‘I’를 찾자마자 dfs(i, j)를 호출하고 곧바로 루프를 종료했다. 이 최적화는 엄청 큰 차이를 만들진 않아도, 불필요한 반복을 줄이고 코드 의도를 명확히 해준다(“시작점 찾으면 끝”).