[BOJ/Java] 23288: 주사위 굴리기 2

2025. 5. 16. 01:34·Problem Solving/BOJ
문제

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

크기가 N x M인 지도가 있다. 지도 상의 정보는 정수로 주어진다.

 

이 지도 상에는 주사위가 하나 있다. 주사위의 전개도(주사위의 처음 상태)는 다음과 같다. 1이 주사위의 윗면이다.

  2
4 1 3
  5
  6

 

지도 상의 주사위 이동은 다음과 같이 진행된다.

 

1. 이동 방향으로 한 칸 구른다. 이동했을 때 칸이 없다면 현재 이동 방향의 반대 방향으로 구른다.

2. 주사위가 도착한 (x, y) 상의 좌표에 대한 점수를 획득한다. 이때 점수는 주사위의 현재 좌표의 숫자와 숫자가 동일한, 인접해서 연속으로 이동할 수 있는 칸의 수를 모두 더한 값이다.

3. 주사위의 아랫면에 있는 정수와 주사위가 도착한 칸의 정수를 비교해 이동 방향을 결정한다.

 

1~1,000번 이동했을 때 획득할 수 있는 점수를 계산하면 된다.

 

풀이

처음엔 한 번 이동 시 연속으로 이동하며 결과값을 반환하는 함수를 구현하려고 했다. 그런데 move 함수에서 BFS로 이동하려니 한 번 이동에 여러 번 움직이는 코드가 되었다. move 함수에서 점수를 계산하는 로직을 분리하여 move 함수는 새로운 좌표를 계산하고 주사위를 굴리도록 변경해주었다. 점수 계산을 위해서는 한 번 이동 후 해당 좌표를 기준으로 그래프를 탐색해주어야 하기 때문에 이쪽이 더 적절한 로직이라고 생각된다.

 

점수를 계산하는 로직도 문제 설명과 아예 달랐다는 점. 문제를 처음 봤을 땐, 이동하는 방향에 따라 모든 칸의 수를 더하는 줄 알았지만, 다시 보니 현재 칸과 동일한 숫자의 칸을 모두 방문해서 더하는 것이 한 번 이동했을 때의 점수였다. 문제를 자세히 읽는 연습이 필요할 것 같다.

 

또 다른 문제는 주사위를 굴리는 로직에서 발생했다. 주사위의 전개도가 가로축, 세로축을 기준으로 일정한 패턴으로 움직이고 있어 조작하기 쉽도록 이차원 배열로 저장했는데, 주사위를 굴렸을 때 가로축과 세로축이 겹치는 주사위의 윗면이 동기화되어 변경되지 않으면서 예상치 못했던 문제가 발생했다.

 

코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int n, m, k;
    static int[][] map;
    static int[] dx = {0, 1, 0, -1};
    static int[] dy = {1, 0, -1, 0};
    static int[][] dice = {
            {4, 1, 3}, // 전개도의 가로축
            {2, 5}, // 전개도의 세로축
            {6} // 전개도 상의 주사위 아랫면
    };
    static int x, y = 0;
    static int dir = 0; // 동, 남, 서, 북

    public static void main(String[] args) throws IOException {
        st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        k = Integer.parseInt(st.nextToken());
        map = new int[n][m];
        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < m; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        int point = 0;
        // k번 이동한다.
        // 1. 이동 방향으로 한 칸 구른다. 만약 막혀있을 경우, 반대로 이동한다.
        // 2. 해당 칸의 점수를 획득한다.
        // 3. 주사위의 아랫면의 수와 해당 칸의 수를 비교해 이동 방향을 결정한다.
        while (k-- > 0) {
            move(); // 이동한다.
            
            point += getScore(x, y); // 이동 후 얻을 수 있는 점수를 계산한다.
            
            // 다음 이동 방향을 결정
            int bottom = dice[2][0]; // 주사위 아랫면 숫자
            int cell = map[x][y]; // 현재 칸의 숫자

            if (bottom > cell) {
                dir = (dir + 1) % 4;
            } else if (bottom < cell) {
                dir = (dir + 3) % 4;
            }
        }

        System.out.println(point);
    }

    public static void move() {
        int nx = x + dx[dir];
        int ny = y + dy[dir];

        if (nx < 0 || nx >= n || ny < 0 || ny >= m) {
            dir = (dir + 2) % 4;
            nx = x + dx[dir];
            ny = y + dy[dir];
        }

        x = nx;
        y = ny;
        
        roll();
    }

    public static void roll() {
        int temp = dice[2][0];
        switch (dir) {
            // 동, 남, 서, 북
            case 0:
                dice[2][0] = dice[0][2]; // 아랫면
                dice[0][2] = dice[0][1];
                dice[0][1] = dice[0][0];
                dice[0][0] = temp;
                break;
            case 1:
                dice[2][0] = dice[1][1];
                dice[1][1] = dice[0][1];
                dice[0][1] = dice[1][0];
                dice[1][0] = temp;
                break;
            case 2:
                dice[2][0] = dice[0][0];
                dice[0][0] = dice[0][1];
                dice[0][1] = dice[0][2];
                dice[0][2] = temp;
                break;
            case 3:
                dice[2][0] = dice[1][0];
                dice[1][0] = dice[0][1];
                dice[0][1] = dice[1][1];
                dice[1][1] = temp;
                break;
        }
    }

    static int getScore(int startX, int startY) {
        boolean[][] visited = new boolean[n][m];
        Queue<int[]> queue = new ArrayDeque<>();
        int value = map[startX][startY];
        int count = 1;

        visited[startX][startY] = true;
        queue.offer(new int[]{startX, startY});

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

            for (int d = 0; d < 4; d++) {
                int newX = currentX + dx[d];
                int newY = currentY + dy[d];

                if (newX < 0 || newY < 0 || newX >= n || newY >= m) continue;
                if (visited[newX][newY]) continue;
                if (map[newX][newY] != value) continue;

                visited[newX][newY] = true;
                count++;
                queue.offer(new int[]{newX, newY});
            }
        }

        return count * value;
    }

}

 

오답 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int n, m, k, point;
    static int[][] map;
    static boolean[][] visited;
    static int[] dx = {0, 1, 0, -1}; // 동, 남, 서, 북
    static int[] dy = {1, 0, -1, 0};
    static int[][] dice = {{4, 1, 3}, {2, 1, 5}, {6}};

    public static void main(String[] args) throws IOException {
        st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        k = Integer.parseInt(st.nextToken());
        map = new int[n][m];
        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < m; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        // k번 이동한다.
        // 1. 이동 방향으로 한 칸 구른다. 만약 막혀있을 경우, 반대로 이동한다.
        // 2. 해당 칸의 점수를 획득한다.
        // 3. 주사위의 아랫면의 수와 해당 칸의 수를 비교해 이동 방향을 결정한다.
        while (k-- > 0) {
            // 첫 번째 이동은 동쪽
            point += map[0][1];
            move(0, 1, 1, changeBottomOfDice(1));
        }

        System.out.println(point);
    }

    public static void move(int startX, int startY, int startDir, int bottom) {
        Queue<Node> queue = new ArrayDeque<>();
        visited = new boolean[n][m];

        visited[startX][startY] = true;
        queue.add(new Node(startX, startY, startDir, bottom));

        while (!queue.isEmpty()) {
            Node current = queue.poll();

            for (int i = 0; i < 4; i++) {
                int newX = current.x + dx[i];
                int newY = current.y + dy[i];

                if (newX < 0 || newX >= n || newY < 0 || newY >= m) {
                    int newDir = (current.dir + 2) % 4;
                    int newBottom = changeBottomOfDice(newDir);
                    newX = current.x + dx[newDir];
                    newY = current.y + dy[newDir];
                    point += map[newX][newY];
                    queue.add(new Node(newX, newY, newDir, newBottom));
                }
                if (bottom > map[newX][newY]) {
                    // 이동 방향을 90도 시계 방향으로 회전
                    int newDir = (current.dir + 1) % 4;
                    int newBottom = changeBottomOfDice(newDir);
                    point += map[newX][newY];
                    queue.add(new Node(newX, newY, newDir, newBottom));
                } else if (bottom < map[newX][newY]) {
                    // 이동 방향을 90도 반시계 방향으로 회전
                    int newDir = current.dir == 1 ? 4 : current.dir - 1;
                    int newBottom = changeBottomOfDice(newDir);
                    point += map[newX][newY];
                    queue.add(new Node(newX, newY, newDir, newBottom));
                } else {
                    // 이동 방향에 변화 없음
                    int newBottom = changeBottomOfDice(current.dir);
                    point += map[newX][newY];
                    queue.add(new Node(newX, newY, current.dir, newBottom));
                }
            }
        }
    }

    public static int changeBottomOfDice(int dir) {
        int temp = dice[2][0];
        switch (dir) {
            // 동, 남, 서, 북
            case 0:
                dice[2][0] = dice[0][2]; // 아랫면
                dice[0][2] = dice[0][1];
                dice[0][1] = dice[0][0];
                dice[0][0] = temp;
                break;
            case 1:
                dice[2][0] = dice[1][2];
                dice[1][2] = dice[1][1];
                dice[1][1] = dice[1][0];
                dice[1][0] = temp;
                break;
            case 2:
                dice[2][0] = dice[0][0];
                dice[0][0] = dice[0][1];
                dice[0][1] = dice[0][2];
                dice[0][2] = temp;
                break;
            case 3:
                dice[2][0] = dice[1][0];
                dice[1][0] = dice[1][1];
                dice[1][1] = dice[1][2];
                dice[1][2] = temp;
                break;
        }
        return dice[2][0];
    }

    public static class Node {
        int x;
        int y;
        int dir;
        int bottom;

        public Node(int x, int y, int dir, int bottom) {
            this.x = x;
            this.y = y;
            this.dir = dir;
            this.bottom = bottom;
        }
    }


}
저작자표시 비영리 동일조건 (새창열림)

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ/Java] 16174: 점프왕 쩰리 (Large)  (0) 2025.05.18
[BOJ/Java] 17951: 흩날리는 시험지 속에서 내 평점이 느껴진거야  (0) 2025.05.16
[BOJ/Java] 5549: 행성 탐사  (0) 2025.05.15
[BOJ/Java] 18113: 그르다 김가놈  (0) 2025.05.14
[BOJ/Java] 20955: 민서의 응급 수술  (0) 2025.05.14
'Problem Solving/BOJ' 카테고리의 다른 글
  • [BOJ/Java] 16174: 점프왕 쩰리 (Large)
  • [BOJ/Java] 17951: 흩날리는 시험지 속에서 내 평점이 느껴진거야
  • [BOJ/Java] 5549: 행성 탐사
  • [BOJ/Java] 18113: 그르다 김가놈
friend5hip
friend5hip
개발 관련 지식이나 기록을 남기고 있습니다.
  • friend5hip
    friend5hip
    friend5hip
  • 전체
    오늘
    어제
    • 분류 전체보기 (60)
      • 컴퓨터공학 (2)
        • 자료구조 (0)
        • 알고리즘 (1)
        • 데이터베이스 (1)
      • Problem Solving (42)
        • BOJ (25)
        • 프로그래머스 (15)
      • 언어 (2)
        • JavaScript (2)
      • 라이브러리 (12)
        • React (12)
      • 개발 (2)
      • 기타 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    구간 합
    BFS
    집합
    프로그래머스
    이분 탐색
    코드트리
    수학
    삼성 sw 역량테스트
    vite
    dp
    react
    매개 변수 탐색
    그리디
    intersection observer
    투 포인터
    JavaScript
    백트래킹
    완전 탐색
    백준
    dfs
    맵
    소수 판별
    구현
    N과 M
    순열
    시뮬레이션
    정렬
    문자열
    java
    누적 합
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
friend5hip
[BOJ/Java] 23288: 주사위 굴리기 2
상단으로

티스토리툴바