본문 바로가기
알고리즘 풀이/백준

[Gold IV] 알고스팟 - 1261 (Java)

by cornsilk-tea 2023. 8. 19.

 

문제 요약

알고스팟 운영진이 모두 미로에 갇혔다. 미로는 N*M 크기이며, 총 1*1 크기의 방으로 이루어져 있다. 미로는 빈 방 또는 벽으로 이루어져 있고, 빈 방은 자유롭게 다닐 수 있지만, 벽은 부수지 않으면 이동할 수 없다.

알고스팟 운영진은 여러명이지만, 항상 모두 같은 방에 있어야 한다. 즉, 여러 명이 다른 방에 있을 수는 없다. 어떤 방에서 이동할 수 있는 방은 상하좌우로 인접한 빈 방이다. 즉, 현재 운영진이 (x, y)에 있을 때, 이동할 수 있는 방은 (x+1, y), (x, y+1), (x-1, y), (x, y-1)이다. 단, 미로의 밖으로 이동할 수는 없다.

벽은 평소에는 이동할 수 없지만, 알고스팟의 무기 AOJ를 이용해 벽을 부수어 버릴 수 있다. 벽을 부수면, 빈 방과 동일한 방으로 변한다.

만약 이 문제가 알고스팟에 있다면, 운영진들은 궁극의 무기 sudo를 이용해 벽을 한 번에 다 없애버릴 수 있지만, 안타깝게도 이 문제는 Baekjoon Online Judge에 수록되어 있기 때문에, sudo를 사용할 수 없다.

현재 (1, 1)에 있는 알고스팟 운영진이 (N, M)으로 이동하려면 벽을 최소 몇 개 부수어야 하는지 구하는 프로그램을 작성하시오.


문제 분석

  • 시작점에서부터 BFS(너비 우선 탐색)을 사용하여 각 위치까지의 최소 벽 부수기 횟수를 계산한다.
  • 우선순위 큐(PriorityQueue)를 사용하여 벽 부수기 횟수가 작은 순서대로 노드를 처리한다.
  • 이를 통해 최소 벽 부수기 횟수로 도착점에 도달하는 경로를 찾는다.
  • 상하좌우로 이동하면서 새로운 위치를 탐색하고, 해당 위치의 정보를 큐에 추가한다.
  • 도착점에 도달하면 최소 벽 부수기 횟수를 업데이트하고 처리를 종료한다.

코드

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

public class Main {
    static int N,M, result;
    static int[][] map;
    static int[] dr = {1, 0, -1, 0};
    static int[] dc = {0, 1, 0, -1};

    static class Node implements Comparable<Node> {
        int r;
        int c;
        int breakCnt;

        public Node(int r, int c, int breakCnt) {
            this.r = r;
            this.c = c;
            this.breakCnt = breakCnt;
        }

        @Override
        public int compareTo(Node o) {
            if(this.breakCnt < o.breakCnt) {
                return -1;
            } else if(this.breakCnt > o.breakCnt) {
                return 1;
            }
            return 0;
        }


    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());;
        M = Integer.parseInt(st.nextToken());
        N = Integer.parseInt(st.nextToken());
        if(N == 1 && M == 1){
            System.out.println(0);
            return;
        }
        map = new int[N][M];
        for(int r = 0; r < N; r++){
            String temp = br.readLine();
            for(int c = 0; c < M; c++){
                map[r][c] = Character.getNumericValue(temp.charAt(c));
            }
        }
//        for(int[] a : map){
//            System.out.println(Arrays.toString(a));
//        }
        result = Integer.MAX_VALUE;
        boolean[][] v = new boolean[N][M];
        v[0][0] = true;
        bfs(0,0,0, v);
        System.out.println(result);
    }

    private static void bfs(int r, int c, int wallCnt, boolean[][] v){
        PriorityQueue<Node> q = new PriorityQueue<>();
        q.add(new Node(r,c,0));
        while(!q.isEmpty()){
            Node node = q.poll();
            for(int d = 0; d < 4; d++) {
                int nr = node.r + dr[d];
                int nc = node.c + dc[d];
                int breakCnt = node.breakCnt;
                if (nr < 0 || nc < 0 || nr >= N || nc >= M) {
                    continue;
                }
                if(nr == N-1 && nc == M-1){
                    // 도착
                    result = Math.min(result, breakCnt);
                    continue;
                }
                if(v[nr][nc] == false){
                    v[nr][nc] = true;
                    q.add(new Node(nr,nc,breakCnt + (map[nr][nc] == 1 ? 1 : 0)));
                }
            }
        }
    }

}

정리

더 찾아볼만한 사항

이전에 비슷한 문제를 덱을 사용한 0-1BFS로 풀었던 적이 있는데 이번 로직과 저번 로직 중 어떤 로직이 더 효율적인지 모르겠다.