ballqs 님의 블로그
[Java] 프로그래머스 문제 - 미로 탈출 본문
오늘 풀어본 문제는 프로그래머스의 미로 탈출 이라는 문제를 풀어보았다.
문제를 보자마자 DFS , BFS 알고리즘 쪽으로 풀면 될거라고 생각했다.
문제 설명
1 x 1 크기의 칸들로 이루어진 직사각형 격자 형태의 미로에서 탈출하려고 합니다. 각 칸은 통로 또는 벽으로 구성되어 있으며, 벽으로 된 칸은 지나갈 수 없고 통로로 된 칸으로만 이동할 수 있습니다. 통로들 중 한 칸에는 미로를 빠져나가는 문이 있는데, 이 문은 레버를 당겨서만 열 수 있습니다. 레버 또한 통로들 중 한 칸에 있습니다. 따라서, 출발 지점에서 먼저 레버가 있는 칸으로 이동하여 레버를 당긴 후 미로를 빠져나가는 문이 있는 칸으로 이동하면 됩니다. 이때 아직 레버를 당기지 않았더라도 출구가 있는 칸을 지나갈 수 있습니다. 미로에서 한 칸을 이동하는데 1초가 걸린다고 할 때, 최대한 빠르게 미로를 빠져나가는데 걸리는 시간을 구하려 합니다.
미로를 나타낸 문자열 배열 maps가 매개변수로 주어질 때, 미로를 탈출하는데 필요한 최소 시간을 return 하는 solution 함수를 완성해주세요. 만약, 탈출할 수 없다면 -1을 return 해주세요.
제한 사항
- 5 ≤ maps의 길이 ≤ 100
- 5 ≤ maps[i]의 길이 ≤ 100
- maps[i]는 다음 5개의 문자들로만 이루어져 있습니다.
- S : 시작 지점
- E : 출구
- L : 레버
- O : 통로
- X : 벽
- 시작 지점과 출구, 레버는 항상 다른 곳에 존재하며 한 개씩만 존재합니다.
- 출구는 레버가 당겨지지 않아도 지나갈 수 있으며, 모든 통로, 출구, 레버, 시작점은 여러 번 지나갈 수 있습니다.
입출력 예
maps | result |
["SOOOL","XXXXO","OOOOO","OXXXX","OOOOE"] | 16 |
["LOOXS","OOOOX","OOOOO","OOOOO","EOOOO"] | -1 |
["SOOOO", "OOOOO", "OOOOO", "OOOOO", "OOOLE"] | 8 |
["OOSOO", "XOXOX", "OLOXO", "XOXOX", "EOOOO"] | 6 |
입출력 예 설명
입출력 예 #1
다음과 같이 이동하면 풀어진다.
풀이 과정
해당 문제를 보자마자 떠올랐던건 DFS , BFS 알고리즘을 사용하자 였다.
다만 오랜만에 봐서인지 헷갈렸지만 기억을 되새겨보며 문제를 풀었고 진행과정은 아래와 같이 생각했다.
- Start 지점이 어디인지 파악하기
- Start 지점부터 너비 우선 탐색인 BFS 알고리즘을 사용하여 탐색
- End에 도달하고 그중 가장 낮은 수치를 리턴
코드(오답)
import java.util.*;
class Solution {
int[] x = {0 , 1 , 0 , -1};
int[] y = {1 , 0 , -1 , 0};
int xSize = 0;
int ySize = 0;
int answer = 0;
public int solution(String[] maps) {
xSize = maps[0].length();
ySize = maps.length;
int[] position = start(maps);
bfs(position[0] , position[1] , maps);
return answer;
}
// Start 자리 찾기
public int[] start(String[] maps) {
int[] result = new int[2];
out : for (int i = 0; i < maps.length; i++) {
int idx = 0;
for (char c : maps[i].toCharArray()) {
if (c == 'S') {
result[0] = idx;
result[1] = i;
break out;
}
idx++;
}
}
return result;
}
// bfs 알고리즘으로 너비 우선 탐색 시작
public void bfs(int startX , int startY , String[] maps) {
Queue<int[]> queue = new LinkedList<>();
// 좌표X , 좌표Y , 거리 , 레버당긴여부
queue.add(new int[]{startX , startY , 0 , 0});
while (queue.size() > 0) {
int[] info = queue.poll();
for (int i = 0; i < 4; i++) {
int newX = info[0] + x[i];
int newY = info[1] + y[i];
// 많이 탐색되는 경우
if (info[2] > 100000) {
answer = 0;
return;
}
if (newX < 0 || newX >= xSize || newY < 0 || newY >= ySize) {
} else {
// 해당 데이터 행을 toCharArray로 배열화 후 찾기
char[] c = maps[newY].toCharArray();
// 목표지점이 E인 경우 && 레버를 당겼을 경우
if (c[newX] == 'E' && info[3] > 0) {
answer = info[2] + 1;
return;
} else if (c[newX] != 'X') { // X(벽)이 아닌 경우는 여기에
if (c[newX] == 'L') { // L이면 레버를 당겼다고 표기
info[3] = 1;
}
// queue에 담는 작업
queue.add(new int[]{newX , newY , info[2] + 1 , info[3]});
}
}
}
}
// 거리가 0이면 -1로 전환
if (answer == 0) {
answer = -1;
}
}
}
으로 생각해서 구현했으나 오답이였다….
그리고 고민을 더 해보니 끊어서 생각하니까 해결방법에 더 가까웠다…
그렇게 구현한게 아래의 코드다.
코드(정답)
import java.util.*;
class Solution {
int[] dx = {0, 1, 0, -1};
int[] dy = {1, 0, -1, 0};
int xSize = 0;
int ySize = 0;
public int solution(String[] maps) {
xSize = maps[0].length();
ySize = maps.length;
// 시작점, 레버, 출구 좌표 찾기
int[] start = findPosition(maps, 'S');
int[] lever = findPosition(maps, 'L');
int[] exit = findPosition(maps, 'E');
// 시작점에서 레버까지의 최소 거리 구하기
int distanceToLever = bfs(maps, start, 'L');
if (distanceToLever == -1) return -1;
// 레버에서 출구까지의 최소 거리 구하기
int distanceToExit = bfs(maps, lever, 'E');
if (distanceToExit == -1) return -1;
// 총 거리 계산
return distanceToLever + distanceToExit;
}
// 특정 문자를 찾는 함수
private int[] findPosition(String[] maps, char target) {
for (int y = 0; y < ySize; y++) {
for (int x = 0; x < xSize; x++) {
if (maps[y].charAt(x) == target) {
return new int[]{x, y};
}
}
}
return null;
}
// BFS로 해당 위치까지의 거리 구하는 메서드
private int bfs(String[] maps, int[] start, char target) {
// boolean 배열을 만든 이유! target까지는 일반적으로 갔던 길을 다시 가지 않음
boolean[][] visited = new boolean[ySize][xSize];
Queue<int[]> queue = new LinkedList<>();
// x, y, 거리
queue.add(new int[]{start[0], start[1], 0});
// 시작지점을 갔다고 표시
visited[start[1]][start[0]] = true;
while (!queue.isEmpty()) {
int[] current = queue.poll();
int x = current[0];
int y = current[1];
int distance = current[2];
// 목표 지점 도달
if (maps[y].charAt(x) == target) {
return distance;
}
// 4방향 탐색
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
// 범위 체크 및 방문 여부 확인
if (nx >= 0 && nx < xSize && ny >= 0 && ny < ySize
&& !visited[ny][nx] && maps[ny].charAt(nx) != 'X') {
visited[ny][nx] = true;
queue.add(new int[]{nx, ny, distance + 1});
}
}
}
// 목표를 찾지 못한 경우
return -1;
}
}
'코딩 공부 > Java' 카테고리의 다른 글
[Java] Proxy 패턴이란? (0) | 2024.09.24 |
---|---|
[Java] 프로그래머스 문제 - 점 찍기 (0) | 2024.08.27 |
[Java] Stack를 활용한 DFS 알고리즘으로 푼 문제 (0) | 2024.08.16 |
[Java] 투 포인터(Two Pointer) 알고리즘이란? (0) | 2024.08.13 |
[Java] Comparator compare() 과 Comparable compareTo() (0) | 2024.08.06 |