안녕하세요. 회사와 함께 성장하고 싶은 KOSE입니다.

 

이번 포스팅은 BFS의 2206번 자바 풀이를 작성하고자 합니다.

BFS는 Queue를 활용하기 때문에 선입 선출로 처리되며 최단 거리를 찾는데 효율적입니다.

 

문제 링크: https://www.acmicpc.net/problem/2206

 

2206번: 벽 부수고 이동하기

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로

www.acmicpc.net

 

 

1. 코드

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

import java.util.ArrayDeque;
import java.util.Queue;

import static java.lang.Integer.parseInt;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] inputs = br.readLine().split(" ");

        int n = parseInt(inputs[0]);
        int m = parseInt(inputs[1]);

        WallMap wallMap = new WallMap(n, m);
        for (int i = 0; i < n; i++) {
            String str = br.readLine();
            for (int j = 0; j < m; j++) {
                wallMap.addMap(i, j, str.charAt(j));
            }
        }

        System.out.println(wallMap.findShortRoad());
    }
}

class WallMap {

    static final int[] D_X = {1, 0, -1, 0};
    static final int[] D_Y = {0, 1, 0, -1};

    char[][] map; // 입력한 벽을 저장할 2차원 배열
    boolean[][][] visited; // 방문 여부 3차원 배열

    class Road {
        int x; // x 좌표
        int y; // y 좌표
        int step; // bfs 단계
        boolean destroyed; // 길에 벽이 있다면 파괴 여부

        Road(int x, int y, int step, boolean destroyed) {
            this.x = x; 
            this.y = y;
            this.step = step;
            this.destroyed = destroyed;
        }

    }

    WallMap(int n, int m) {
        map = new char[n][m];
        visited = new boolean[n][m][2];
    }

    void addMap(int x, int y, char c) {
        map[x][y] = c;
    }
 
    int findShortRoad() {
        Queue<Road> queue = new ArrayDeque<>(); // 큐 생성
        queue.add(new Road(0, 0, 1, false)); // 첫 번째 길 등록 (x = 0, y = 0, step = 1, 파괴 X)

        // bfs
        while(!queue.isEmpty()) {
            Road road = queue.poll(); // 큐에서 저장한 Road 인스턴스 빼기

            if (road.x == map.length - 1 && road.y == map[0].length - 1) {
                return road.step; // 만약 찾아야 하는 마지막 배열에 위치한 경우 종료
            }

            for (int k = 0; k < D_X.length; k++) {
                int newX = road.x + D_X[k]; // dx dy 
                int newY = road.y + D_Y[k];

                if (validRange(newX, newY)) continue; // 인덱스 유효한 배열 확인

                if (map[newX][newY] == '0') { // 만약 map에 있는 값이 벽이 아니라면
                    if (!road.destroyed && !visited[newX][newY][0]) {
                        
                        // 큐에 new Road()로 새로운 길의 x, y, step + 1, 벽 파괴 X 입력하기
                        queue.add(new Road(newX, newY, road.step + 1, false));
                        visited[newX][newY][0] = true; // 방문 여부 등록
                    }

                    // 만약 길이 파괴된 상태, 방문하지 않은 경우 true를 queue에 넣기
                    else if (road.destroyed && !visited[newX][newY][1]) {
                        queue.add(new Road(newX, newY, road.step + 1, true));
                        visited[newX][newY][1] = true;
                    }
                }

                // 만약 새로운 길이 벽이라면, 파괴되지 않은 경우만 큐에 넣기
                else if (map[newX][newY] == '1') {
                    if (!road.destroyed) {
                        queue.add(new Road(newX, newY, road.step + 1, true));
                        visited[newX][newY][1] = true;
                    }
                }
            }
        }
        return -1;
    }

    boolean validRange(int x, int y) {
        return x < 0 || y < 0 || x >= map.length || y >= map[0].length;
    }


}

 

 

 

2. 풀이 중 어려웠던 점

 

이전 BFS 문제를 해결할 때는 인스턴스 배열을 생성해서 그 안에 인스턴스를 넣고 초기화하는 작업을 수행했습니다. 만약 입력 시점에 1이 입력된다면 possibleBreakWall 리스트에 추가하는 방법을 했습니다.

 

하지만, 시간초과가 발생했고 비효율적인 방법이었다는 점을 확인했습니다. 만약, Road 배열을 선언하고 그 안에 road 인스턴스를 넣는다면 벽을 깬 방법이 올바르지 않을 때, 매번 배열에 등록된 인스턴스를 전부 초기화하는 작업을 수행했어야 했습니다.

 

class WallMap {

    Road[][] roads;
    List<Road> possibleBreakWall = new ArrayList<>();

 

이 문제를 해결하기 위해,

따라서, char[][] map이라는 배열을 등록하여, Queue에 넣을 때마다 인스턴스에 상태를 기록하는 방법을 적용했습니다.

 

이 방법의 장점은

BFS는 계속 순회하되, map이라는 벽을 기록한 상태 정보는 바뀌지 않기 때문에
Queue에 등록되는 인스턴스의 벽을 깼는지 여부를 파악하고 다음 스텝에 새로운 Road 인스턴스를 생성하며 현재 스텝 + 1, 벽 깬 여부 true만 이어서 등록하면 됩니다.

 

즉, Queue에 있는 road는 해당 좌표와 현재 벽을 깼는지 그리고 방문한 상태인지만 파악한다면 

1 -> 2 -> 3(벽 깸)  ->  5  ->  7 (벽 못깸)

           -> 4          ->  6   ->  7(벽 깸)  -> 도착지

           -> 9(벽 깸)  -> 4 이미 2번 노드의 자식 노드인 4번 노드에서 먼저 방문 했으므로 종료

 

이런식으로 처리 될 수 있습니다.

 

이상입니다!! 감사합니다!!

 

 

+ Recent posts