백준 18352는 그냥 BFS 최단거리 문제다.
모든 도로 길이가 1이라서 다익스트라까지 갈 필요 없고,
출발 도시 X에서 BFS 한 번 돌린 뒤 distance 배열에서 값이 K인 도시만 뽑으면 끝난다.
문제 자체는 어렵지 않은데,
막상 구현할 때는 여기서 한 번씩 꼬인다.
- distance 배열을 어떻게 둘지
- 방문 체크를 따로 할지 같이 할지
- 큐를 리스트로 할지 deque로 할지
- 거리 갱신을 뭘 기준으로 할지
나도 처음엔 방향은 맞게 잡았는데
거리 갱신을 잘못 잡아서 한 번 틀어졌다.
그래서 이 문제는 정답만 보는 것보다, BFS 거리 문제를 어떻게 구현하는지 정리해두는 게 더 중요했다.
문제 정리
문제 조건은 이렇다.
- 도시가 N개 있다
- 도로가 M개 있다
- 도로는 방향성이 있다
- 모든 도로 길이는 1
- 출발 도시 X에서 **최단거리 정확히 K*인 도시를 출력한다
- 없으면 1 출력
여기서 바로 봐야 하는 건 두 개다.
- 최단거리
- 모든 간선 가중치가 1
이 조합이면 BFS로 가는 게 맞다.
🔗 문제 링크
백준 18352 - 특정 거리의 도시 찾기
https://www.acmicpc.net/problem/18352
정답
from collections import deque
n, m, k, x = map(int, input().split())
graph = [[] for _ in range(n + 1)]
for _ in range(m):
a, b = map(int, input().split())
graph[a].append(b)
distance = [-1] * (n + 1)
distance[x] = 0
q = deque([x])
while q:
now = q.popleft()
for nxt in graph[now]:
if distance[nxt] == -1:
distance[nxt] = distance[now] + 1
q.append(nxt)
result = []
for city in range(1, n + 1):
if distance[city] == k:
result.append(city)
if result:
for city in result:
print(city)
else:
print(-1)
생각해야 하는 부분
이 문제는 BFS만 떠올리면 끝나는 게 아니다.
정확히는 BFS + distance 배열 설계가 핵심이다.
1. 왜 DFS가 아니라 BFS인가
이 문제는 “최단거리 K”를 묻는다.
DFS는 깊게 들어갔다가 돌아오는 구조라서
“출발점에서 몇 단계 떨어졌는지”를 깔끔하게 관리하기 불편하다.
반면 BFS는
- 1칸 떨어진 도시
- 2칸 떨어진 도시
- 3칸 떨어진 도시
이렇게 레벨 순서대로 탐색하니까
최단거리 문제와 바로 연결된다.
즉, 이 문제는 거리 문제라서 BFS다.
2. 왜 distance 배열을 따로 두는가
이 문제에서 distance 배열은 그냥 거리 저장용이 아니다. 사실상 방문 체크 + 최단거리 기록을 같이 한다.
distance = [-1] * (n + 1)
distance[x] = 0
이 구조로 가면 의미가 명확하다.
- 1 → 아직 방문 안 함
- 0 이상 → 방문했고, 그 값이 최단거리
처음엔 방문 리스트를 따로 둘까 싶었는데,
이 문제는 distance 하나로 끝내는 게 제일 깔끔했다.
3. 거리 갱신 기준은 “도시 번호”가 아니라 “이전 거리”다
이 문제에서 제일 중요한 줄은 이거다.
distance[nxt] = distance[now] + 1
핵심은 도시 번호가 아니라
출발점에서 현재 도시까지의 거리다.
즉,
- now가 몇 번 도시인지는 중요하지 않고
- now까지 몇 칸 왔는지가 중요하다
그래서 다음 도시는 항상 +1로 갱신된다.
4. 왜 deque를 쓰는가
BFS니까 큐가 필요하다.
파이썬에서는 큐가 필요하면 그냥 deque 쓰는 게 맞다.
from collections import deque
q = deque([x])
now = q.popleft()
리스트로 pop(0) 해도 돌아가긴 하는데,
앞에서 계속 꺼내는 구조라 비효율적이다.
이 문제는 입력 크기도 있어서
괜히 리스트 큐로 버티는 것보다 deque로 가는 게 낫다.
예외처리 / 구현할 때 주의할 점
⚠️ 1. 방향 그래프다
이 문제는 도로가 방향성을 가진다.
graph[a].append(b)
여기서 graph[b].append(a)까지 넣으면
양방향 그래프가 돼서 완전히 다른 결과가 나온다.
⚠️ 2. 도시 번호는 1번부터 시작한다
그래서 배열 길이는 n + 1로 두는 게 맞다.
graph = [[] for _ in range(n + 1)]
distance = [-1] * (n + 1)
이걸 n으로 잡으면 인덱스가 바로 꼬인다.
⚠️ 3. 정답은 방문 순서가 아니라 거리 K인 도시다
BFS를 돌고 나서
방문한 순서 출력하는 문제가 아니다.
마지막에는 반드시
for city in range(1, n + 1):
if distance[city] == k:
print(city)
이렇게 distance 값을 보고 판단해야 한다.
내 시행착오
이 문제에서 내가 처음 틀린 건 방향이 아니라 구현 디테일이었다.
1. distance 배열을 이상하게 만들었다
처음엔 거리 배열을 문자열처럼 잡으려고 했다.
dis = [f'inf({i})' for i in range(n)]
지금 보면 거리 배열이 아니라 그냥 문자열 리스트다.
이건 비교도 안 되고, 갱신도 이상해진다.
결국 이 문제는 숫자로 가야 해서
- 1 초기화가 제일 깔끔했다.
2. 큐를 리스트로 두고 pop(0)을 썼다
todo = []
todo.append(x)
now = todo.pop(0)
동작은 할 수 있는데,
이 방식은 BFS 구현할 때 계속 찝찝하다.
앞에서 꺼내는 일이 반복되니까
그냥 deque.popleft()로 가는 게 맞다.
3. 거리 갱신을 도시 번호 기준으로 생각했다
처음엔 이런 식으로 꼬였다.
dis[now] = now + 1
근데 이건 도시 번호에 1 더한 거지,
최단거리가 아니다.
이 문제는
“지금 몇 번 도시냐”가 아니라
“출발점에서 몇 칸 떨어져 있냐”를 기록해야 한다.
즉, 도시 번호랑 거리 개념을 섞으면 바로 틀어진다.
한 줄 정리
이 문제는 BFS 거리 문제의 기본형이다. 핵심은 BFS 자체보다 distance 배열을 방문 여부 + 최단거리 기록용으로 같이 쓰는 것이다.
'Coding Test > 문제풀이' 카테고리의 다른 글
| 프로그래머스 신고 결과 받기 | 구현 문제에서 문제 해석이 먼저인 이유 (0) | 2026.04.30 |
|---|---|
| 프로그래머스 완주하지 못한 선수 | set 대신 dictionary를 써야 하는 이유 (0) | 2026.04.23 |
| 프로그래머스 파일명 정렬 | 정규식으로 HEAD, NUMBER, TAIL 나누는 방법 (0) | 2026.04.22 |
| 얼음 얼려 먹기 문제 정리 | DFS로 연결 요소 개수 세는 방법 (0) | 2026.04.21 |
| 백준 2178 미로 탐색 | 좌표형 BFS 구현과 최단거리 풀이 (0) | 2026.04.17 |
