가장 가까운 충전소까지의 최단 거리
자바스크립트 코딩테스트 문제로 bfs 주제를 연습해보세요. 난이도는 medium이며, 브라우저에서 바로 JavaScript로 풀이를 실행할 수 있습니다.
2차원 격자 grid가 주어집니다. 각 칸은 다음 셋 중 하나입니다.
'C': 충전소'E': 이동 가능한 빈 칸'W': 벽
상하좌우로 한 칸씩만 이동할 수 있을 때, 각 이동 가능한 칸에서 가장 가까운 충전소까지의 최단 거리를 2차원 배열로 반환하세요.
- 충전소 칸의 거리는
0입니다. - 벽 칸은 결과에서도
-1로 표시합니다. - 어떤 빈 칸이 어느 충전소와도 연결되지 않으면 그 칸도
-1로 표시합니다. - 충전소가 하나도 없으면 모든 이동 가능한 칸은
-1입니다.
제한사항
1 <= grid.length <= 2001 <= grid[i].length <= 200- 각 칸의 값은
'C','E','W'중 하나입니다. grid는 직사각형 형태입니다.- 이동은 상하좌우 네 방향만 가능합니다.
- 반환 배열의 크기는 입력
grid와 같아야 합니다.
예시
- 입력:
[ ['E', 'E', 'C'], ['W', 'E', 'E'], ['E', 'E', 'E'] ]출력:
[ [2, 1, 0], [-1, 2, 1], [4, 3, 2] ] - 입력:
[ ['E', 'W', 'E'], ['W', 'W', 'W'], ['C', 'E', 'E'] ]출력:
[ [-1, -1, -1], [-1, -1, -1], [0, 1, 2] ]
힌트
- 각 빈 칸마다 충전소를 따로 찾으면 너무 느릴 수 있습니다.
- 한 충전소에서만 시작하지 말고, 모든 충전소에서 동시에 출발한다고 생각해 보세요.
- BFS는 먼저 도착한 거리가 항상 최단 거리라는 점을 이용할 수 있습니다.
해설
이 문제는 격자에서 최단 거리를 구하는 전형적인 BFS 문제입니다. 다만 각 빈 칸마다 가장 가까운 충전소를 따로 찾으려고 하면 비효율적입니다.
예를 들어 빈 칸이 많고 충전소도 여러 개라면, 각 칸마다 BFS를 다시 돌리는 방식은 시간 초과로 이어질 수 있습니다.
이럴 때는 관점을 뒤집는 것이 핵심입니다.
1. 모든 충전소를 동시에 시작점으로 둡니다
우리가 원하는 것은 “각 칸에서 가장 가까운 충전소까지의 거리”입니다.
이 말은 반대로 보면 “각 충전소에서 퍼져 나가며 처음 도달한 거리”와 같습니다.
그래서 모든 'C' 칸을 큐에 먼저 넣고 거리를 0으로 시작합니다.
2. BFS로 바깥으로 한 겹씩 확장합니다
BFS는 거리 0인 칸들, 거리 1인 칸들, 거리 2인 칸들 순서로 확장됩니다. 따라서 어떤 빈 칸을 처음 방문한 순간의 거리가 그 칸의 최단 거리입니다.
진행 방식은 다음과 같습니다.
- 결과 배열을 전부
-1로 초기화합니다. grid를 순회하면서'C'는 결과에0을 기록하고 큐에 넣습니다.'W'는 그대로-1로 둡니다.
- 큐에서 칸을 하나 꺼내 상하좌우를 봅니다.
- 아직 방문하지 않은
'E'칸이면 현재 거리 + 1을 기록하고 큐에 넣습니다. - BFS가 끝나면 방문되지 않은
'E'칸은 연결되지 않은 칸이므로-1그대로 남습니다.
3. 왜 이 풀이가 맞을까요?
BFS는 항상 더 짧은 거리부터 처리합니다.
여기서는 모든 충전소를 한꺼번에 시작점으로 넣었기 때문에, 어떤 빈 칸에 가장 먼저 도달한 경로는 모든 충전소 중 가장 가까운 충전소에서 온 최단 경로입니다.
즉, 나중에 더 긴 거리로 다시 도착할 가능성은 있어도 더 짧은 거리로 도착할 수는 없습니다. 그래서 첫 방문 때 기록한 값이 정답입니다.
4. 복잡도
- 격자의 각 칸은 최대 한 번만 큐에 들어갑니다.
- 시간 복잡도:
O(rows * cols) - 공간 복잡도:
O(rows * cols)
칸마다 BFS를 다시 돌리는 방식보다 훨씬 효율적입니다.
function solution(grid) {
const rows = grid.length;
const cols = grid[0].length;
const dist = Array.from({ length: rows }, () => Array(cols).fill(-1));
const queue = [];
let head = 0;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (grid[r][c] === 'C') {
dist[r][c] = 0;
queue.push([r, c]);
}
}
}
const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]];
while (head < queue.length) {
const [r, c] = queue[head++];
for (const [dr, dc] of directions) {
const nr = r + dr;
const nc = c + dc;
if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue;
if (grid[nr][nc] !== 'E') continue;
if (dist[nr][nc] !== -1) continue;
dist[nr][nc] = dist[r][c] + 1;
queue.push([nr, nc]);
}
}
return dist;
}
코드 작성
starter code를 바탕으로 함수를 완성한 뒤 예제 테스트를 실행해보세요.
커스텀 테스트
함수 인자를 JSON 배열 형태로 입력하세요. 예: [3, 5], [[1, 2, 3]]
실행 결과
아직 실행하지 않았습니다.
댓글
문제 풀이 아이디어, 질문, 반례를 자유롭게 나눠보세요.