본문 바로가기

백준 문제풀이

[Baekjoon 16928] 뱀과 사다리 게임 - JAVA

728x90

[Gold V] 뱀과 사다리 게임 - 16928

문제 링크

성능 요약

메모리: 14340 KB, 시간: 104 ms

분류

너비 우선 탐색, 그래프 이론, 그래프 탐색

제출 일자

2025년 2월 1일 20:04:55

문제 설명

뱀과 사다리 게임을 즐겨 하는 큐브러버는 어느 날 궁금한 점이 생겼다.

주사위를 조작해 내가 원하는 수가 나오게 만들 수 있다면, 최소 몇 번만에 도착점에 도착할 수 있을까?

게임은 정육면체 주사위를 사용하며, 주사위의 각 면에는 1부터 6까지 수가 하나씩 적혀있다. 게임은 크기가 10×10이고, 총 100개의 칸으로 나누어져 있는 보드판에서 진행된다. 보드판에는 1부터 100까지 수가 하나씩 순서대로 적혀져 있다.

플레이어는 주사위를 굴려 나온 수만큼 이동해야 한다. 예를 들어, 플레이어가 i번 칸에 있고, 주사위를 굴려 나온 수가 4라면, i+4번 칸으로 이동해야 한다. 만약 주사위를 굴린 결과가 100번 칸을 넘어간다면 이동할 수 없다. 도착한 칸이 사다리면, 사다리를 타고 위로 올라간다. 뱀이 있는 칸에 도착하면, 뱀을 따라서 내려가게 된다. 즉, 사다리를 이용해 이동한 칸의 번호는 원래 있던 칸의 번호보다 크고, 뱀을 이용해 이동한 칸의 번호는 원래 있던 칸의 번호보다 작아진다.

게임의 목표는 1번 칸에서 시작해서 100번 칸에 도착하는 것이다.

게임판의 상태가 주어졌을 때, 100번 칸에 도착하기 위해 주사위를 굴려야 하는 횟수의 최솟값을 구해보자.

입력

첫째 줄에 게임판에 있는 사다리의 수 N(1 ≤ N ≤ 15)과 뱀의 수 M(1 ≤ M ≤ 15)이 주어진다.

둘째 줄부터 N개의 줄에는 사다리의 정보를 의미하는 x, y (x < y)가 주어진다. x번 칸에 도착하면, y번 칸으로 이동한다는 의미이다.

다음 M개의 줄에는 뱀의 정보를 의미하는 u, v (u > v)가 주어진다. u번 칸에 도착하면, v번 칸으로 이동한다는 의미이다.

1번 칸과 100번 칸은 뱀과 사다리의 시작 또는 끝이 아니다. 모든 칸은 최대 하나의 사다리 또는 뱀을 가지고 있으며, 동시에 두 가지를 모두 가지고 있는 경우는 없다. 항상 100번 칸에 도착할 수 있는 입력만 주어진다.

출력

100번 칸에 도착하기 위해 주사위를 최소 몇 번 굴려야 하는지 출력한다.

문제 풀이

해당 문제의 그래프를 구성하면 아래와 같다.

1번 칸부터 100번 칸까지 각각에 대해, 주사위를 굴렸을 때 갈 수 있는 최대 6가지 경우를 고려해야 한다.

만약 해당 칸에 사다리/뱀이 있다면, 도착 즉시 사다리/뱀을 타고 이동한 최종 위치가 된다.

예를 들어, 4번 칸에서 주사위 결과가 6이라면 10번 칸에 도착하는데, 이 칸이 사다리의 시작이라면 사다리를 타고 목적지로 이동한다.

BFS 탐색은 아래와 같다.

시작점인 1번 칸에서 BFS를 시작한다.

큐(queue)에 현재 칸과 그때까지 주사위를 굴린 횟수를 함께 저장한다.

매번 큐에서 원소를 꺼내, 주사위 1~6에 대해 다음 칸을 계산한다.

만약 100번 칸을 넘어서면 이동 불가이므로 스킵한다.

그렇지 않고 도착 칸에 사다리/뱀이 있으면 해당 칸을 바로 타서 도착하는 칸으로 간주한다.

이미 더 적은 횟수로 방문한 적이 있으면 넘어가고, 방문하지 않았거나 현재가 더 적은 횟수라면 갱신하고 큐에 넣어준다.

100번 칸에 도착하는 순간의 횟수가 최소 횟수가 된다(그래프에서 BFS의 특징).

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.Queue;
import java.util.StringTokenizer;

class Main {
    static int N, M;
    static int[] visited;
    static ArrayList<ArrayList<Integer>> graph = new ArrayList<>();

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

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        visited = new int[101];

        Arrays.fill(visited, Integer.MAX_VALUE);

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

        for (int i = 0; i < N + M; i++) {
            st = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());

            graph.get(x).add(y);
        }

        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[] {1, 0});
        visited[1] = 0;

        while(!queue.isEmpty()) {
            int[] current = queue.poll();
            int x = current[0];
            int count = current[1];

            for (int i = 1; i <= 6; i++) {
                int nx = x + i;

                if (nx <= 100) {

                    if (graph.get(nx).isEmpty()) {
                        if (visited[nx] > count + 1) {
                            visited[nx] = count + 1;
                            queue.offer(new int[] {nx, count + 1});
                        }
                    }
                    else {
                        for (int warp = 0; warp < graph.get(nx).size(); warp++) {
                            int next = graph.get(nx).get(warp);
                            if (visited[next] > count + 1) {
                                visited[next] = count + 1;
                                queue.offer(new int[] {next, count + 1});
                            }
                        }
                    }
                }
            }
        }

        System.out.println(visited[100]);

    }
}

코드 풀이

    static int N, M;
    static int[] visited;
    static ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
  • N, M은 각각 사다리 수, 뱀 수를 저장할 변수
  • visited[i]: i번 칸에 도달하기 위해 주사위를 굴린 최소 횟수를 저장할 배열
  • graph[i]: i번 칸에서 사다리나 뱀이 있을 경우 이동할 도착 칸 정보를 저장할 배열
        visited = new int[101];
        Arrays.fill(visited, Integer.MAX_VALUE);

        for (int i = 0; i < 101; i++) {
            graph.add(new ArrayList<>());
        }
  • Integer.MAX_VALUE로 초기화해 아직 방문하지 않았음을 표시한다.
  • graph.get(x)에 y를 넣으면 x번 칸에 도착했을 때 y로 바로 이동하도록 그래프를 초기화시킨다.
        for (int i = 0; i < N + M; i++) {
            st = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());

            graph.get(x).add(y);
        }
  • 입력으로부터 사다리와 뱀 정보를 받아서 그래프에 기록
  • graph.get(x)가 비어있지 않다면, 사다리나 뱀이 있음을 의미한다.
        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[] {1, 0});
        visited[1] = 0;

        while(!queue.isEmpty()) {
            int[] current = queue.poll();
            int x = current[0];
            int count = current[1];

            for (int i = 1; i <= 6; i++) {
                int nx = x + i;

                if (nx <= 100) {

                    if (graph.get(nx).isEmpty()) {
                        if (visited[nx] > count + 1) {
                            visited[nx] = count + 1;
                            queue.offer(new int[] {nx, count + 1});
                        }
                    }
                    else {
                        for (int warp = 0; warp < graph.get(nx).size(); warp++) {
                            int next = graph.get(nx).get(warp);
                            if (visited[next] > count + 1) {
                                visited[next] = count + 1;
                                queue.offer(new int[] {next, count + 1});
                            }
                        }
                    }
                }
            }
        }

        System.out.println(visited[100]);

    }
  • queue에는 BFS를 위한 [현재 칸 번호, 주사위를 굴린 횟수] 저장할 용도로 초기화
  • queue에 시작점을 지정해준다.
  • 큐에서 현재 칸과 그동안 굴린 주사위 횟수를 꺼낸다.
  • x에는 현재 칸을 저장하고, count에는 현재까지 주사위를 굴린 횟수를 저장하는 변수로 사용한다.
  • 1부터 6까지 던져서 나온 칸 nx를 확인한다.
  • nx가 100 이상이라면 넘어간다.
  • nx에 사다리 또는 뱀이 있다면, 이동해야 할 칸(목적 칸)으로 다시 설정한다.
  • visited[목적 칸] 값이 기존보다 큰(방문하지 않았거나 더 많은 횟수로 기록되어 있었던) 경우에만 방문 갱신하고 큐에 삽입해준다.
  • 100번 칸을 방문하는 순간이 최소 횟수이기에 출력해준다.
728x90