문제


 

 

 

 

풀이


2차원 평면이 주어질 때, 출발지에서 목적지까지의 단순 거리를 BFS 탐색을 통해 알아내는 문제다.

가장 기본적인 BFS 문제 유형에서 1개의 벽을 부술 수 있다는 한 가지 조건이 추가된 문제다.

 

추가된 위 조건으로 인해 생긴 고려사항은 다음과 같다.

  1. 기존에 (x, y) 좌표와 거리 값을 담는 distance 변수 외에 해당 좌표로 오기까지 벽을 부순 횟수를 저장할 변수가 하나 더 필요하다.
  2. 탐색할 다음 좌표가 벽이더라도 지금까지 벽을 부순 횟수가 0이라면 벽을 부수고 지날 수 있다.
  3. 기존에는 visited 변수를 통해 해당 좌표를 방문했는지만 체크했지만, 여기선 추가로 방문 여부 + 벽을 부순 횟수를 체크해야한다.
    따라서, visited 변수를 boolean 타입이 아닌 int 타입으로 선언함.

 

위의 세 번째 조건을 보자. 방문했는지의 여부와 벽을 부순 횟수를 추가로 저장해야하는데 다음 그림을 보자.

 

 

BFS로 탐색하면 길을 탐색할텐데, 하나는 벽을 부수고 왔고, 하나는 안 부수고 왔다. 이때는 안 부수고 온 쪽을 기준으로 한다.

벽을 한 번도 안 부수고온 쪽이 먼저 탐색했다면, visited[ny][nx]에는 0이 담기므로 벽을 부수고온 쪽에서는 탐색할 필요가 없다.

벽을 한 번 부수고온 쪽이 먼저 탐색한다면, visited[ny][nx]에는 1이 담기므로 벽을 안 부수고온 쪽에서 새로 탐색한다.

if (visited[ny][nx] > point.drill) {
    ....
}

 

 

 

코드


import java.io.*;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {

    private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    private static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    private static int N, M;
    private static int[][] map;
    private static int[][] visited;
    private static int[] dx = {0, 0, -1, 1};
    private static int[] dy = {-1, 1, 0, 0};

    static class Point {
        int x, y, distance;
        int drill; // 공사 횟수

        public Point(int x, int y, int distance, int drill) {
            this.x = x;
            this.y = y;
            this.distance = distance;
            this.drill = drill;
        }
    }

    public static void main(String[] args) throws IOException {
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        map = new int[N][M];
        visited = new int[N][M];

        for (int i = 0; i < N; i++) {
            String str = br.readLine();
            for (int j = 0; j < M; j++) {
                map[i][j] = Integer.parseInt(String.valueOf(str.charAt(j)));
                visited[i][j] = Integer.MAX_VALUE;
            }
        }

        int ans = bfs(0, 0);
        bw.write(ans + "\n");

        bw.flush();
        bw.close();
        br.close();
    }

    private static int bfs(int x, int y) {
        Queue<Point> q = new LinkedList<>();
        q.add(new Point(x, y, 1, 0)); // (1, 1)에서 시작
        visited[y][x] = 0; // 처음 공사 횟수

        while (!q.isEmpty()) {
            Point point = q.poll();

            // 도착지점을 만났다면 종료한다.
            if (point.x == M - 1 && point.y == N - 1)
                return point.distance;

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

                if (nx >= 0 && nx < M && ny >= 0 && ny < N) {
                    if (visited[ny][nx] > point.drill) {
                        if (map[ny][nx] == 0) { // 벽이 아닐 때
                            q.add(new Point(nx, ny, point.distance + 1, point.drill));
                            visited[ny][nx] = point.drill;
                        } else { // 벽일 때
                            if (point.drill == 0) { // 지금까지 벽을 부순 횟수가 0이라면 
                                q.add(new Point(nx, ny, point.distance + 1, point.drill + 1));
                                visited[ny][nx] = point.drill + 1;
                            }
                        }
                    }
                }
            }
        }

        return -1;
    }
}