알고리즘/[ Baekjoon ]

[ BOJ ][JAVA][9328] 열쇠

kim.svadoz 2021. 9. 3. 17:42
반응형

https://www.acmicpc.net/problem/9328

 

9328번: 열쇠

상근이는 1층 빌딩에 침입해 매우 중요한 문서를 훔쳐오려고 한다. 상근이가 가지고 있는 평면도에는 문서의 위치가 모두 나타나 있다. 빌딩의 문은 모두 잠겨있기 때문에, 문을 열려면 열쇠가

www.acmicpc.net

시간 제한 메모리 제한 제출 정답 맞은 사람 정답 비율
1 초 256 MB 10710 2990 1907 25.122%

문제

상근이는 1층 빌딩에 침입해 매우 중요한 문서를 훔쳐오려고 한다. 상근이가 가지고 있는 평면도에는 문서의 위치가 모두 나타나 있다. 빌딩의 문은 모두 잠겨있기 때문에, 문을 열려면 열쇠가 필요하다. 상근이는 일부 열쇠를 이미 가지고 있고, 일부 열쇠는 빌딩의 바닥에 놓여져 있다. 상근이는 상하좌우로만 이동할 수 있다.

상근이가 훔칠 수 있는 문서의 최대 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수가 주어진다. 테스트 케이스의 수는 100개를 넘지 않는다.

각 테스트 케이스의 첫째 줄에는 지도의 높이와 너비 h와 w (2 ≤ h, w ≤ 100)가 주어진다. 다음 h개 줄에는 빌딩을 나타내는 w개의 문자가 주어지며, 각 문자는 다음 중 하나이다.

  • '.'는 빈 공간을 나타낸다.
  • '*'는 벽을 나타내며, 상근이는 벽을 통과할 수 없다.
  • '$'는 상근이가 훔쳐야하는 문서이다.
  • 알파벳 대문자는 문을 나타낸다.
  • 알파벳 소문자는 열쇠를 나타내며, 그 문자의 대문자인 모든 문을 열 수 있다.

마지막 줄에는 상근이가 이미 가지고 있는 열쇠가 공백없이 주어진다. 만약, 열쇠를 하나도 가지고 있지 않는 경우에는 "0"이 주어진다.

상근이는 처음에는 빌딩의 밖에 있으며, 빌딩 가장자리의 벽이 아닌 곳을 통해 빌딩 안팎을 드나들 수 있다. 각각의 문에 대해서, 그 문을 열 수 있는 열쇠의 개수는 0개, 1개, 또는 그 이상이고, 각각의 열쇠에 대해서, 그 열쇠로 열 수 있는 문의 개수도 0개, 1개, 또는 그 이상이다. 열쇠는 여러 번 사용할 수 있다.

출력

각 테스트 케이스 마다, 상근이가 훔칠 수 있는 문서의 최대 개수를 출력한다.

예제 입력 1

3
5 17
*****************
.............**$*
*B*A*P*C**X*Y*.X.
*y*x*a*p**$*$**$*
*****************
cz
5 11
*.*********
*...*...*x*
*X*.*.*.*.*
*$*...*...*
***********
0
7 7
*ABCDE*
X.....F
W.$$$.G
V.$$$.H
U.$$$.J
T.....K
*SQPML*
irony

예제 출력 1

3
1
0

코드

/*
    열쇠 (Gold1)
    BFS

    ver1: 새로운 열쇠를 얻으면 BFS를 처음부터 다시 실행시키려 했으나 TLE
    ver2: 열쇠를 못찾은 문은 따로 저장해두어, 해당 열쇠를 찾았을 때 그 문의 위치에서 BFS를 실행토록 함
*/
import java.io.*;
import java.util.*;

public class p9328 {
    static class Node {
        int r, c;
        public Node(int r, int c) {
            this.r = r;
            this.c = c;
        }
    }
    static int T, h, w, answer;
    static int[] dr = {-1, 0, 1, 0};
    static int[] dc = {0, -1, 0, 1};
    static char[][] map;
    static List<List<Node>> gates; // 열지 못한 문을 저장
    static HashMap<Character, Character> keyMap;
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        T = Integer.parseInt(br.readLine());

        StringBuilder sb = new StringBuilder();
        StringTokenizer st;
        while (T-- > 0) {
            st = new StringTokenizer(br.readLine());
            h = Integer.parseInt(st.nextToken());
            w = Integer.parseInt(st.nextToken());
            answer = 0;
            map = new char[h][w];
            for (int i = 0; i < h; i++) {
                String line = br.readLine();
                for (int j = 0; j < w; j++) {
                    map[i][j] = line.charAt(j);
                }
            }

            gates = new ArrayList<>();
            for (int i = 0; i < 26; i++) {
                gates.add(new ArrayList<>());
            }

            String hasKey = br.readLine();
            keyMap = matchKey(hasKey);
            go();
            sb.append(answer).append("\n");
        }
        System.out.println(sb.toString());
    }

    static HashMap<Character, Character> matchKey(String s) {
        HashMap<Character, Character> hm = new HashMap<>();

        if (s.equals("0")) return hm;

        for (int i = 0; i < s.length(); i++) {
            char letter = (char)((int)(s.charAt(i)));
            hm.put(Character.toUpperCase(letter), letter);
        }

        return hm;
    }

    static void go() {
        Queue<Node> q = new LinkedList<>();
        boolean[][] visited = new boolean[h][w];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                if (i == 0 || i == h - 1 || j == 0 || j == w - 1) {
                    char letter = map[i][j];
                    if (letter == '*') continue;

                    if (97 <= letter && letter <= 122) {
                        keyMap.put(Character.toUpperCase(letter), letter);
                        visited[i][j] = true;
                        q.add(new Node(i, j));
                    } else if (letter == '$') {
                        map[i][j] = '.';
                        q.add(new Node(i, j));
                        visited[i][j] = true;
                        answer++;
                    } else if (letter == '.') {
                        visited[i][j] = true;
                        q.add(new Node(i, j));
                    }

                    if (65 <= letter && letter <= 90) {
                        if (keyMap.containsKey(letter)) {
                            visited[i][j] = true;
                            q.add(new Node(i, j));
                        } else {
                            gates.get(letter - 'A').add(new Node(i, j));
                        }
                    }
                }
            }
        }

        while (!q.isEmpty()) {
            Node cur = q.poll();
            int r = cur.r;
            int c = cur.c;
            for (int i = 0; i < 4; i++) {
                int nr = r + dr[i];
                int nc = c + dc[i];
                if (OOB(nr, nc)) continue;
                char letter = map[nr][nc];
                if (letter == '*') continue;
                if (visited[nr][nc]) continue;

                if (97 <= letter && letter <= 122) { // 새로운 소문자 열쇠를 발견했으므로 keyMap에 추가한다.
                    q.add(new Node(nr, nc));
                    visited[nr][nc] = true;
                    map[nr][nc] = '.';
                    keyMap.put(Character.toUpperCase(letter), letter);

                    // 혹시 이 열쇠로 열 수 있는 문이 있는지 없는지 확인한다!!!(시간 절약)
                    for (Node node : gates.get(letter - 'a')) {
                        map[node.r][node.c] = '.';
                        visited[node.r][node.c] = true;
                        q.add(new Node(node.r, node.c));
                    }
                } else if (letter == '$') {
                    q.add(new Node(nr, nc));
                    visited[nr][nc] = true;
                    map[nr][nc] = '.';
                    answer++;
                } else if (letter == '.') {
                    q.add(new Node(nr, nc));
                    visited[nr][nc] = true;
                } else if (65 <= letter && letter <= 90) {
                    if (keyMap.containsKey(letter)) { // 해당 대문자에 대한 열쇠를 가지고 있으므로 문을 열수 있다.
                        q.add(new Node(nr, nc));
                        map[nr][nc] = '.';
                        visited[nr][nc] = true;
                    } else {
                        // 아직 해당 문에 대한 열쇠가 없다면, 문을 저장해놓는다.
                        gates.get(letter - 'A').add(new Node(nr, nc));
                    }
                }


            }
        }
    }

    static boolean OOB(int r, int c) {
        return r < 0 || c < 0 || r >= h || c >= w;
    }
}
반응형