반응형
Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 자바의 정석
- LocalDate
- EC2배포
- 자바의정석
- 알고리즘
- 둘만의 암호
- AWS배포
- BFS
- Ubuntu서버
- 분할정복
- spring security 설정
- 혼공얄코
- 스프링부트 배포
- spring security
- DFS
- 프로그래머스 2개 이하로 다른 비트
- systemd
- 티스토리챌린지
- 백트래킹
- 오블완
- Comparable
- 프로그래머스 둘만의 암호
- 분할정복 알고리즘
- 서버초기설정
- hackerrank
- 리눅스
- SQL Mapper
- 프로그래머스
- java
- 둘만의 암호 자바
Archives
- Today
- Total
쉽게 쉽게
[프로그래머스] 미로 탈출 (Java) - BFS 본문
반응형
📌 핵심 요약
- 경유지 최단거리 분해 — "S에서 E로 가되 반드시 L을 거쳐야 한다"는 조건은 S→L 최단거리 + L→E 최단거리로 쪼갤 수 있다. 경유지가 고정이면 두 BFS 결과를 독립적으로 합쳐도 최단이 보장된다.
- BFS는 target 문자만 알면 된다 — 목표 좌표를 미리 저장할 필요 없이, 탐색 중 처음 만난 target까지의 거리가 곧 최단거리다.
1 문제 분석
Programmers #159993
미로 탈출
S(시작점)에서 출발해 E(출구)로 탈출해야 한다. 단, 출구는 잠겨 있어서 반드시 L(레버)을 먼저 당겨야 열린다. X는 벽이라 지나갈 수 없고, 한 칸 이동에 1초가 걸린다. 탈출까지 걸리는 최소 시간을 구하고, 탈출할 수 없으면 -1을 반환한다.
풀이 과정
- 미로를 순회하며 S와 L의 좌표를 기록한다. (E는 BFS가 문자로 직접 찾으므로 저장 불필요)
- 1차 BFS: S에서 출발해 L까지의 최단거리를 구한다. 도달 불가면 -1 반환.
- 2차 BFS: L에서 출발해 E까지의 최단거리를 구한다. 도달 불가면 -1 반환.
- 두 최단거리의 합을 반환한다.
2 접근: BFS를 두 번으로 나누기
이 문제의 핵심은 "레버를 거쳐야 한다"는 제약을 어떻게 처리하느냐다.
상태에 레버를 당겼는지 여부를 추가해서 한 번의 BFS로 풀 수도 있지만, 훨씬 단순한 관점이 있다.
경유지 L이 고정되어 있다는 점에 주목하면, 전체 경로는 반드시 "S에서 L까지의 구간"과 "L에서 E까지의 구간"으로 나뉜다. 각 구간의 최단거리는 서로 독립적이므로, 두 구간을 각각 BFS로 구해 더하면 그것이 전체 최단 시간이 된다.
왜 두 최단거리를 더해도 최단이 보장될까?
전체 경로가 L을 지나는 순간, 경로는 (S→L 부분)과 (L→E 부분)으로 쪼개진다. 만약 전체 최단 경로의 S→L 부분이 S→L 최단거리보다 길다면, 그 부분만 최단 경로로 교체해서 더 짧은 전체 경로를 만들 수 있으므로 모순이다. 따라서 각 구간의 최단거리 합 = 전체 최단거리가 성립한다. 경유지가 하나로 고정일 때만 통하는 논리라는 점에 주의.또 하나의 설계 포인트는 BFS에 목표 좌표가 아닌 목표 문자를 넘긴다는 것이다.
BFS는 거리 순으로 칸을 방문하므로, 탐색 중 target 문자를 처음 만난 시점의 거리가 곧 최단거리다.
덕분에 E의 좌표를 미리 저장할 필요가 없고, 같은 bfs 메서드를 두 구간에 그대로 재사용할 수 있다.
3 전체 코드
import java.util.*;
class Solution {
// 상하좌우 4방향 이동량 (행 변화, 열 변화)
static final int[] dx = {-1, 1, 0, 0};
static final int[] dy = {0, 0, -1, 1};
public int solution(String[] maps) {
int rows = maps.length;
int cols = maps[0].length();
char[][] map = new char[rows][cols];
int[] start = null; // 시작 지점 S 좌표
int[] lever = null; // 레버 L 좌표
// 문자열 배열 → char 2차원 배열 변환하며 S, L 좌표 기록
// (E는 BFS가 target 문자로 직접 찾으므로 좌표 저장 불필요)
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
char c = maps[i].charAt(j);
map[i][j] = c;
if (c == 'S') start = new int[]{i, j};
else if (c == 'L') lever = new int[]{i, j};
}
}
// 1차 BFS: 시작점 → 레버 최단거리
int toLever = bfs(start, 'L', map);
if (toLever == -1) return -1; // 레버 도달 불가 → 탈출 불가
// 2차 BFS: 레버 → 출구 최단거리
int toExit = bfs(lever, 'E', map);
if (toExit == -1) return -1; // 출구 도달 불가 → 탈출 불가
// 경유지(L)가 고정이므로 두 최단거리의 합이 전체 최단 시간
return toLever + toExit;
}
/** start 좌표에서 target 문자까지의 최단거리 반환 (도달 불가 시 -1) */
private int bfs(int[] start, char target, char[][] map) {
int rows = map.length;
int cols = map[0].length;
Queue<int[]> queue = new ArrayDeque<>();
boolean[][] visited = new boolean[rows][cols];
// 큐 원소는 {행, 열, 시작점부터의 거리}
queue.add(new int[]{start[0], start[1], 0});
visited[start[0]][start[1]] = true; // 방문 표시는 enqueue 시점에!
while (!queue.isEmpty()) {
int[] cur = queue.poll();
int row = cur[0], col = cur[1], dist = cur[2];
// 목표 도달 → BFS 특성상 처음 만난 거리가 곧 최단거리
if (map[row][col] == target) return dist;
for (int d = 0; d < 4; d++) {
int nextRow = row + dx[d];
int nextCol = col + dy[d];
// 격자 범위를 벗어나면 스킵
if (nextRow < 0 || nextRow >= rows || nextCol < 0 || nextCol >= cols) continue;
// 벽(X)이면 스킵
if (map[nextRow][nextCol] == 'X') continue;
// 이미 방문한 칸이면 스킵
if (visited[nextRow][nextCol]) continue;
visited[nextRow][nextCol] = true;
queue.add(new int[]{nextRow, nextCol, dist + 1});
}
}
return -1; // 큐가 비었는데 target 미도달
}
}
방문 표시는 queue 삽입 직전에
방문 표시를 poll 시점에 하면 같은 칸이 큐에 여러 번 들어가 시간과 메모리를 낭비한다. "확인 → 방문 표시 → 큐 삽입" 순서를 지켜, 큐에 넣는 순간 방문 처리하는 것이 BFS의 기본 패턴이다.| 잘못된 내용이 있다면 지적부탁드립니다. 방문해주셔서 감사합니다. |

반응형
'알고리즘 > BFS (너비 우선 탐색)' 카테고리의 다른 글
| [프로그래머스] 거리두기 확인하기(Java) — BFS (0) | 2026.07.18 |
|---|---|
| [프로그래머스] 무인도 여행 — BFS (1) | 2026.07.16 |
| [프로그래머스] 전력망을 둘로 나누기 (Java) — BFS (0) | 2026.07.03 |
| [프로그래머스] 리코쳇 로봇(Java) - BFS (1) | 2026.01.27 |
| [프로그래머스] 타겟 넘버(DFS/BFS) -Java (0) | 2025.12.03 |
