[백준]1600_말이 되고픈 원숭이

김피자·2023년 6월 12일
0

백준

목록 보기
39/42

문제

동물원에서 막 탈출한 원숭이 한 마리가 세상구경을 하고 있다. 그 녀석은 말(Horse)이 되기를 간절히 원했다. 그래서 그는 말의 움직임을 유심히 살펴보고 그대로 따라 하기로 하였다. 말은 말이다. 말은 격자판에서 체스의 나이트와 같은 이동방식을 가진다. 다음 그림에 말의 이동방법이 나타나있다. x표시한 곳으로 말이 갈 수 있다는 뜻이다. 참고로 말은 장애물을 뛰어넘을 수 있다.

근데 원숭이는 한 가지 착각하고 있는 것이 있다. 말은 저렇게 움직일 수 있지만 원숭이는 능력이 부족해서 총 K번만 위와 같이 움직일 수 있고, 그 외에는 그냥 인접한 칸으로만 움직일 수 있다. 대각선 방향은 인접한 칸에 포함되지 않는다.

이제 원숭이는 머나먼 여행길을 떠난다. 격자판의 맨 왼쪽 위에서 시작해서 맨 오른쪽 아래까지 가야한다. 인접한 네 방향으로 한 번 움직이는 것, 말의 움직임으로 한 번 움직이는 것, 모두 한 번의 동작으로 친다. 격자판이 주어졌을 때, 원숭이가 최소한의 동작으로 시작지점에서 도착지점까지 갈 수 있는 방법을 알아내는 프로그램을 작성하시오.

입력

첫째 줄에 정수 K가 주어진다. 둘째 줄에 격자판의 가로길이 W, 세로길이 H가 주어진다. 그 다음 H줄에 걸쳐 W개의 숫자가 주어지는데, 0은 아무것도 없는 평지, 1은 장애물을 뜻한다. 장애물이 있는 곳으로는 이동할 수 없다. 시작점과 도착점은 항상 평지이다. W와 H는 1이상 200이하의 자연수이고, K는 0이상 30이하의 정수이다.

출력

첫째 줄에 원숭이의 동작수의 최솟값을 출력한다. 시작점에서 도착점까지 갈 수 없는 경우엔 -1을 출력한다.

예제 입력 1

1
4 4
0 0 0 0
1 0 0 0
0 0 1 0
0 1 0 0

예제 출력 1

4

예제 입력 2

2
5 2
0 0 1 1 0
0 0 1 1 0

예제 출력 2

-1

풀이

출발지부터 도착지까지 움직일 수 있는 최소 값을 구하는 문제로 BFS를 사용했다.
말이 움직일 수 있는 위치와 원숭이가 움직일 수 있는 위치를 배열로 만들었고, 최소값을 계산할 때에는 한번 방문한 위치는 다시 방문하지 않도록 visited 배열을 선언하였다.

꼭!!! 한 번 방문한 곳은 다시 방문할 수 없다는 것을 기억하자!!
기존 BFS를 풀던 방식처럼 2차원으로 선언하여 풀면 인접 노드에 방문했을 때와 말이 이동할 수 있는 위치로 이동했을 때 이를 구별할 수 없어 무조건 한번 방문한 곳은 다시 방문할 수 없도록 확인해주어야 한다.

visited 배열을 3차원으로 선언하여 서로 다른 경로로 이동했을 때의 처리를 해주었다.
visited[x][y][k번 이동한 수]

k가 0보다 크면 말로 이동할 수 있는 위치로 움직일 수 있도록 이동 시켜주고, 그렇지 않으면 인접 노드로만 이동하도록 하였다.

코드

import java.util.*;
 
public class Main {
    
    static int k, w, h;
    static int[][] board;
    static int min = Integer.MAX_VALUE;
    static int[] hdx = {-2, -2, -1, -1, 1, 1, 2, 2};
    static int[] hdy = {-1, 1, -2, 2, -2, 2, -1, 1};
    static int[] dx = {0, 1, 0 ,-1}; 
    static int[] dy = {1, 0, -1, 0};
    static boolean[][][] visited;
    
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
    
        k = scan.nextInt();
        w = scan.nextInt();
        h = scan.nextInt();
        
        board = new int[h][w];
        for(int i = 0; i < h; i++) {
            for(int j = 0; j < w; j++) {
                board[i][j] = scan.nextInt();
            }
        }
        
        visited = new boolean[h][w][k + 1];
        min = bfs(0, 0);
        
        if(min == Integer.MAX_VALUE) System.out.println("-1");
        else System.out.println(min);
    }
    
    public static int bfs(int x, int y) {
        Queue<Node> q = new LinkedList<>();
        q.offer(new Node(x, y, 0, k)); 
        visited[x][y][k] = true;
        
        while(!q.isEmpty()) {
            Node current = q.poll();
            if(current.x == h - 1 && current.y == w - 1) return current.count; 
            
            for(int i = 0; i < 4; i++) {
                int nx = current.x + dx[i];
                int ny = current.y + dy[i];
                if(nx >= 0 && ny >= 0 && nx < h && ny < w && !visited[nx][ny][current.k] && board[nx][ny] == 0) {
                    visited[nx][ny][current.k] = true;
                    q.offer(new Node(nx, ny, current.count + 1, current.k));
                }
            }
            
            if(current.k > 0) {
                for(int i = 0; i < 8; i++) {
                    int nx = current.x + hdx[i];
                    int ny = current.y + hdy[i];
                    if(nx >= 0 && ny >= 0 && nx < h && ny < w && !visited[nx][ny][current.k - 1] && board[nx][ny] == 0) {
                        visited[nx][ny][current.k - 1] = true;
                        q.offer(new Node(nx, ny, current.count + 1, current.k - 1));
                    }
                }
            }
        }
        return min;
    }
    
    public static class Node {
        int x;
        int y;
        int count;
        int k;
        
        public Node(int x, int y, int count, int k) {
            this.x = x;
            this.y = y;
            this.count = count;
            this.k = k;
        }
    }
}

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

profile
제로부터시작하는코딩생활

0개의 댓글