본문 바로가기
알고리즘/알고리즘 오답노트

백준 9376 탈옥

by shinyou1024 2020. 5. 21.

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

 

9376번: 탈옥

문제 상근이는 감옥에서 죄수 두 명을 탈옥시켜야 한다. 이 감옥은 1층짜리 건물이고, 상근이는 방금 평면도를 얻었다. 평면도에는 모든 벽과 문이 나타나있고, 탈옥시켜야 하는 죄수의 위치도 �

www.acmicpc.net

이 문제는 공부 삼아 블로그를 참고해서 풀었다 (https://stack07142.tistory.com/145 님 블로그)
정말 혼자서는 생각도 못 해낼 방법인 듯...
많이 배웠다!

기본

  • 기본적으로 이 문제에서 가중치는 연 문의 개수이다 (Pos 객체의 door변수로 체크)
  • visit 배열을 방문/미방문이 아니라 몇 개의 문을 열어 해당 지점에 도착했는지를 기록한다
  • 더 적은 가중치(연 문의 수)로 해당 지점을 재방문할 수 있을 때에만 큐에 넣어준다

여기까지는 일반적인 BFS와 동일하다

심화

  • 일반 BFS로 풀면 시간초과가 난다!
  • 문제에 힌트가 있는데, 죄수 2명 외에 상근이라는 외부인이 존재한다 => 이 친구를 player에 집어 넣는다
  • map을 넓혀서 테두리를 모두 '.'으로 채우고 (0,0)에 상근이가 위치한다고 가정한다
  • 상근이와 죄수2명 각각 BFS를 돌린다 (총 3번의 BFS)
  • BFS는 visit기록을 리턴하고 이 기록을 각각의 배열에 저장한다
  • 각 배열의 요소를 더한 게 최소가 되는 지점을 찾는다 => 답

예외처리

  • 만약 셋이 문에서 만나게 될 경우 열어야 할 문은 하나인데 +3이 되어버린다 => 답에서 -2를 해줘야 한다

소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static class Pos {
        int x, y, door;

        public Pos(int x, int y, int door) {
            super();
            this.x = x;
            this.y = y;
            this.door = door;
        }

        @Override
        public String toString() {
            return "Pos [x=" + x + ", y=" + y + ", door=" + door + "]";
        }

    }

    static int[] dx = { -1, 0, 1, 0 };
    static int[] dy = { 0, 1, 0, -1 };
    static char[][] map = new char[105][105];
    static int h, w;

    public static void main(String[] args) throws NumberFormatException, IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = null;
        int T = Integer.parseInt(br.readLine());
        for (int tc = 1; tc <= T; tc++) {
            st = new StringTokenizer(br.readLine());
            h = Integer.parseInt(st.nextToken()) + 2;
            w = Integer.parseInt(st.nextToken()) + 2;

            List<Pos> player = new ArrayList<Main.Pos>();
            player.add(new Pos(0, 0, 0));
            for (int i = 1; i < h - 1; i++) {
                map[i][0] = '.';
                map[i][h - 1] = '.';
                String input = br.readLine();
                for (int j = 1; j < w - 1; j++) {
                    map[i][j] = input.charAt(j - 1);
                    // System.out.println(i+", "+j);
                    // System.out.println(Arrays.toString(map[i]));
                    if (map[i][j] == '$') {
                        player.add(new Pos(i, j, 0));
                    }
                }

            }

            for (int j = 0; j < w; j++) {
                map[0][j] = map[h - 1][j] = '.';
            }
            int[][] dist1 = bfs(player.get(0));
            int[][] dist2 = bfs(player.get(1));
            int[][] dist3 = bfs(player.get(2));

            int ans = Integer.MAX_VALUE;
            int sum = 0;
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    if (map[i][j] == '*')
                        continue;
                    sum = dist1[i][j] + dist2[i][j] + dist3[i][j];
                    if (map[i][j] == '#')
                        sum -= 2;
                    ans = Math.min(ans, sum);
                }
            }
            System.out.print(ans + "\n");
        }
    }

    static int[][] bfs(Pos start) {
        int[][] visit = new int[h][w]; // 몇 개의 문을 열었는지 기록
        Queue<Pos> q = new LinkedList<Pos>();
        memset(visit, -1);
        visit[start.x][start.y] = 0;
        q.offer(start);

        while (!q.isEmpty()) {
            Pos now = q.poll();
            int x = now.x;
            int y = now.y;
            int door = now.door;
            //System.out.println(x + ", " + y);
            for (int i = 0; i < 4; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];
                int ndoor = door;
                if (nx < 0 || nx > h - 1 || ny < 0 || ny > w - 1)
                    continue;
                if (map[nx][ny] == '*')
                    continue;
                if (map[nx][ny] == '#') { // 문일 경우
                    ndoor++;
                }
                if (visit[nx][ny] == -1 || visit[nx][ny] > ndoor) {
                    visit[nx][ny] = ndoor;
                    q.offer(new Pos(nx, ny, ndoor));
                }
            }
        }

        return visit;
    }

    static void memset(int[][] arr, int val) {
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                arr[i][j] = val;
            }
        }
    }
}

'알고리즘 > 알고리즘 오답노트' 카테고리의 다른 글

SWEA 5650 핀볼게임  (0) 2020.05.30
백준 1525 퍼즐  (0) 2020.05.30
백준 1062 가르침  (0) 2020.05.21
SWEA 2112 보호필름  (0) 2020.05.20
백준 16137/SWEA 4727 견우와 직녀  (0) 2020.05.16

댓글