[OLD] 기존 글 저장

프로그래머스 연습문제 풀이 : 미로 탈출 (JAVA)

한민기 2023. 2. 28. 17:00
반응형

문제 링크 : https://school.programmers.co.kr/learn/courses/30/lessons/159993

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

❓  문 제

1 x 1 크기의 칸들로 이루어진 직사각형 격자 형태의 미로에서 탈출하려고 합니다. 각 칸은 통로 또는 벽으로 구성되어 있으며, 벽으로 된 칸은 지나갈 수 없고 통로로 된 칸으로만 이동할 수 있습니다. 통로들 중 한 칸에는 미로를 빠져나가는 문이 있는데, 이 문은 레버를 당겨서만 열 수 있습니다. 레버 또한 통로들 중 한 칸에 있습니다. 따라서, 출발 지점에서 먼저 레버가 있는 칸으로 이동하여 레버를 당긴 후 미로를 빠져나가는 문이 있는 칸으로 이동하면 됩니다. 이때 아직 레버를 당기지 않았더라도 출구가 있는 칸을 지나갈 수 있습니다. 미로에서 한 칸을 이동하는데 1초가 걸린다고 할 때, 최대한 빠르게 미로를 빠져나가는데 걸리는 시간을 구하려 합니다.

미로를 나타낸 문자열 배열 maps가 매개변수로 주어질 때, 미로를 탈출하는데 필요한 최소 시간을 return 하는 solution 함수를 완성해주세요. 만약, 탈출할 수 없다면 -1을 return 해주세요.

 

✏️  제 한  사 항

  • 5 ≤ maps의 길이 ≤ 100
    • 5 ≤ maps[i]의 길이 ≤ 100
    • maps[i]는 다음 5개의 문자들로만 이루어져 있습니다.
      • S : 시작 지점
      • E : 출구
      • L : 레버
      • O : 통로
      • X : 벽
    • 시작 지점과 출구, 레버는 항상 다른 곳에 존재하며 한 개씩만 존재합니다.
    • 출구는 레버가 당겨지지 않아도 지나갈 수 있으며, 모든 통로, 출구, 레버, 시작점은 여러 번 지나갈 수 있습니다.

💻  입 출 력  예

❗️ 입 출 력  예  설 명

주어진 문자열은 다음과 같은 미로이며

다음과 같이 이동하면 가장 빠른 시간에 탈출할 수 있습니다.

4번 이동하여 레버를 당기고 출구까지 이동하면 총 16초의 시간이 걸립니다. 따라서 16을 반환합니다.

 

입출력 예 #2

주어진 문자열은 다음과 같은 미로입니다.

시작 지점에서 이동할 수 있는 공간이 없어서 탈출할 수 없습니다. 따라서 -1을 반환합니다.


💡 문 제  풀 이

미로 찾기 문제는 bfs를 사용하여 문제를 해결할 수 있다. 하지만 이번에는 큐를 사용하여 문제를 해결 한다.

 

문제에서 파악할 수 있는 요건은 아래와 같다.

  • 시작지점에서 도착지점까지 가기 전엔 레버를 꼭 들려야 한다.

문제 요점에 따라 시작지점에서 레버가 있는 곳까지 도착할 수 없다면 -1 을 출력하면 된다.

 

for(int i =0 ; i < height; i++){
    Arrays.fill(visit[i], -1);
}
for(int i = 0; i < height; i++){
    for(int j = 0; j < width; j++){
        map[i][j] = maps[i].charAt(j);
    }
}

 

visit 배열은 시작지점에서 현재 도착한 지점까지의 거리를 넣을 배열이다.

예를들어 내가 있는 곳의 visit[x][y] 값이 3이라면 출발지점에서 현재 x, y까지의 거리는 3 이라는 의미가 된다.

 

결국 visit[레버의 x좌표][레버의 y좌표] + visit[도착지점의 x좌표][도착지점의 y좌표]를 하면 시작지점에서 레버를 거쳤다가 도착지점까지 가는 최종 거리를 알 수 있다.

 

Position start = findPos('S');
Position lever = findPos('L');
Position end = findPos('E');

findPos 함수는 해당 문구를 가진 위치를 찾는 함수이며 결과 값은 x, y 좌표를 가지고 있는 Position 객체를 리턴한다.

 

//시작지점부터 레버까지 경로
bfs(start, lever);
int tmp = visit[lever.y][lever.x];

if(tmp == -1) return -1;
answer += tmp;

bfs(a, b) 함수는 a부터 b까지의 이동거리를 측정하는 함수이다.

첫번째 예제를 보면 시작지점의 visit[y][x] 값이 0, 레버가 있는 곳의 [y][x] 값이 4로 나온것을 볼 수 있으며 시작지점에서 레버까지의 이동 거리가 총 4라는 것을 파악할 수 있다.

 

또한 visit[y][x] 의 값이 -1 일 경우 도착할 수 없는 곳이라는 의미이기 때문에 -1을 리턴한다.

 

visit = new int[height][width];
for(int i = 0; i < height; i++){
    Arrays.fill(visit[i], -1);
}

bfs(lever, end);

tmp = visit[end.y][end.x];
if(tmp == -1) return -1;
answer += tmp;

시작지점에서 레버까지 거리를 구했다면 visit 배열을 다시 초기화 한 후 레버부터 끝지점까지의 거리를 구한다.

 

static void bfs(Position start, Position end){
    Queue<Position> q = new LinkedList<>();
    q.add(start);

    visit[start.y][start.x] = 0;
    if(start.x == end.x && start.y == end.y) return;

    while(!q.isEmpty()){
        Position now = q.poll();

        for(int i = 0; i < 4; i++){
            int nx = now.x + dx[i];
            int ny = now.y + dy[i];

            if(checkPosition(nx, ny) && visit[ny][nx] == -1){
                q.add(new Position(ny, nx));
                visit[ny][nx] = visit[now.y][now.x] + 1;
            }

            if(end.x == nx && end.y == ny) return;
        }

    }

bfs 함수는 실제 start 부터 end까지 도달할 때 모든 좌표의 거리를 저장하는 함수이다.

 

현재 위치의 값을 가져와 상, 하, 좌, 우 한칸씩 이동이 가는한지 파악 후 이동이 가능하다면 해당 좌표의 값을 +1 증가시켜주며 한칸을 이동했다는 것을 의미한다.

 

⌨️ 풀 이  코 드

import java.util.Arrays;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;

public class 미로탈출 {
    public static void main(String[] args) {
        Solution_미로 s = new Solution_미로();

        String[] maps = {"SOOOL","XXXXO","OOOOO","OXXXX","OOOOE"};

        System.out.println(s.solution(maps)); //16
    }
}

class Solution_미로 {
    static char[][] map;
    static int[][] visit;
    static int width;
    static int height;

    static int[] dx = {0 ,0, -1, 1};
    static int[] dy = {-1, 1, 0, 0};

    public int solution(String[] maps) {
        int answer = 0;
        width = maps[0].length();
        height = maps.length;

        map = new char[height][width];
        visit = new int[height][width];

        for(int i =0 ; i < height; i++){
            Arrays.fill(visit[i], -1);
        }
        for(int i = 0; i < height; i++){
            for(int j = 0; j < width; j++){
                map[i][j] = maps[i].charAt(j);
            }
        }
        Position start = findPos('S');
        Position lever = findPos('L');
        Position end = findPos('E');

        //시작지점부터 레버까지 경로
        bfs(start, lever);
        int tmp = visit[lever.y][lever.x];

        if(tmp == -1) return -1;
        answer += tmp;

        visit = new int[height][width];
        for(int i = 0; i < height; i++){
            Arrays.fill(visit[i], -1);
        }

        bfs(lever, end);

        tmp = visit[end.y][end.x];
        if(tmp == -1) return -1;
        answer += tmp;

        return answer;
    }

    static void bfs(Position start, Position end){
        Queue<Position> q = new LinkedList<>();
        q.add(start);

        visit[start.y][start.x] = 0;
        if(start.x == end.x && start.y == end.y) return;

        while(!q.isEmpty()){
            Position now = q.poll();

            for(int i = 0; i < 4; i++){
                int nx = now.x + dx[i];
                int ny = now.y + dy[i];

                if(checkPosition(nx, ny) && visit[ny][nx] == -1){
                    q.add(new Position(ny, nx));
                    visit[ny][nx] = visit[now.y][now.x] + 1;
                }

                if(end.x == nx && end.y == ny) return;
            }

        }
    }

    static boolean checkPosition(int x, int y){
        return x >= 0 && x < width && y >= 0 && y < height && map[y][x] != 'X';
    }

    static Position findPos(char c){
        for(int i = 0; i < map.length; i++){
            for(int j = 0; j < map[0].length; j++){
                if(c == map[i][j]) return new Position(i, j);
            }
        }
        return new Position(-1, -1);
    }

    static class Position{
        int x, y;

        public Position(int y, int x){
            this.y = y;
            this.x = x;
        }
    }

}

 

✅ 비 고

재귀함수는 계속해서 들어가는 거지만 queue를 사용하여 대기열처럼 사용하다 보니 디버깅 할 때도 다음 순서가 어디이며 어디까지 진행했는지 파악하기가 수월하다.

 

반응형