업데이트:

문제 링크

백준 2206번 - 벽 부수고 이동하기 (Gold 4)

문제 설명

N x M 크기의 방의 (1, 1)에서 (N, M)까지 최단 경로로 이동하려고 한다.
벽을 최대 한 개까지만 부술 수 있다고 할 때, 최단 경로의 길이를 구하는 문제이다.

정답 코드 및 설명

역시 최단 경로를 구하는 문제이므로 BFS를 쓰면 된다.
벽을 한 개까지만 부술 수 있다는 점을 고려하는 것이 핵심 포인트이다.
벽을 한 개 부수고 도착하는 경로와 부수지 않고 도착하는 경로 두 개 모두가 존재하는 경우를 고려해야 한다.
이를 위해 벽을 부수고 도착했는지 여부를 판정하는 변수 check0을 하나 더 도입하였다.
아래 코드처럼 풀지 않고 3차원 배열로 체크해도 된다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import java.io.*;
import java.util.*;

public class Main {

    final static int[] xAdd = { -1, 1, 0, 0 }, yAdd = { 0, 0, -1, 1 };

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        int[][] map = new int[n][m];
        for (int i = 0; i < n; i++) {
            String row = br.readLine();
            for (int j = 0; j < m; j++)
                map[i][j] = row.charAt(j) - '0';
        }
        bw.write(minDist(map, n, m) + "");
        bw.close();
    }

    static int minDist(int[][] map, int n, int m) {
        if (n == 1 && m == 1)
            return 1;
        Path[][] path = new Path[n][m];
        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[] { 0, 0, 0 });
        path[0][0] = new Path(1, map[0][0]);
        while (!queue.isEmpty()) {
            int[] curr = queue.poll();
            int x = curr[0], y = curr[1], wall = curr[2];
            int dist = path[x][y].getDist(wall);
            for (int i = 0; i < 4; i++) {
                int newX = x + xAdd[i];
                int newY = y + yAdd[i];
                // 범위를 벗어날 수는 없다
                if (newX < 0 || newX >= n || newY < 0 || newY >= m)
                    continue;
                // 이미 벽을 한번 부쉈는데 또 벽을 통과할 수는 없다
                if ((wall & map[newX][newY]) == 1)
                    continue;
                // 한번도 안 와본 지점인 경우
                if (path[newX][newY] == null) {
                    int newWall = wall | map[newX][newY];
                    queue.add(new int[] { newX, newY, newWall });
                    path[newX][newY] = new Path(dist + 1, newWall);
                    // 최단거리에 도달한 경우
                    if (newX == n - 1 && newY == m - 1)
                        return path[newX][newY].minDist();
                    continue;
                }
                // 이미 벽 뚫고 와본 지점이지만 벽을 안 뚫고도 올 수 있는 경우
                if (!path[newX][newY].check0 && wall == 0 && map[newX][newY] == 0) {
                    queue.add(new int[] { newX, newY, wall });
                    path[newX][newY].setDist(dist + 1);
                    continue;
                }
            }
        }
        return -1;
    }
}

class Path {
    int dist0, dist1;
    boolean check0; // 벽을 부수고 온 경로인지를 체크하기 위함

    Path(int dist, int wall) {
        // 벽을 부수고 온 최단 경로
        if (wall == 1) {
            dist1 = dist;
            check0 = false;
        }
        // 벽을 부수지 않고 온 최단 경로
        if (wall == 0) {
            dist0 = dist1 = dist;
            check0 = true;
        }
    }

    void setDist(int dist) {
        dist0 = dist;
        check0 = true;
    }

    int getDist(int wall) {
        if (wall == 1)
            return dist1;
        return dist0;
    }

    int minDist() {
        if (dist0 != 0)
            return dist0;
        return dist1;
    }
}

태그: ,

카테고리:

업데이트:

댓글남기기