Patrick's Devlog

[BOJ/C++] 주사위 굴리기(14499번) 본문

Algorithm/Algorithms Practice

[BOJ/C++] 주사위 굴리기(14499번)

Patrick_ 2025. 1. 22. 13:23

1. 개요

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

1-1. 설명

크기가 N X M인 지도가 존재한다. 지도의 오른쪽은 동쪽, 위쪽은 북쪽이다. 지도 위에 주사위가 놓여져 있으며 전개도는 아래와 같다.

 

지도의 좌표는 (r, c)로 r은 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로부터 떨어진 칸의 개수다.

주사위의 가장 처음에는 모든 면에 0이 적혀있고, 지도에는 각 칸의 정수가 쓰여져 있다. 주사위를 굴렸을 때, 이동한 칸에 쓰여있는 수가 0이면, 주사위의 바닥면에 쓰여있는 수가 칸에 복사된다. 이동한 칸이 0이 아닌 경우에는 쓰여있는 수가 주사위 바닥면으로 복사되며, 칸에 쓰여있는 수는 0이 된다. 

주사위를 놓은 곳의 좌표와 이동시키는 명령이 주어졌을 때, 주사위가 이동했을 때마다 상단에 쓰여있는 값을 구하는 프로그램을 작성한다. 단, 주사위는 지도 바깥으로 이동시킬 수 없으며 이러한 경우 명령을 무시해야 한다.

1-2. 제한 사항

 - 첫줄의 지도 세로 크기 N, 가로 크기 M, 주사위를 놓은 곳의 좌표 x, y, 명령어의 개수 K가 주어짐

 - N과 M은 1 이상 20 이하, x는 0 이상 N-1 이하, y는 0 이상 M-1 이하, K는 1 이상 1,000 이하

 - 둘째 줄부터 N개의 줄에 지도에 쓰여있는 수가 북쪽 -> 남쪽, 각 줄은 서쪽 -> 동쪽 순서대로 주어짐

 - 주사위를 놓는 칸에 쓰여있는 수는 항상 0

 - 지도 각 칸에 쓰여있는 수는 0 이상 10 미만

 - 명령은 1, 2, 3, 4로 이루어져 있으며 각각 동쪽, 서쪽, 북쪽, 남쪽을 의미


2. 구현

2-1. 풀이

문제를 잘 읽고 그대로 구현하면 된다. 헤맸던 부분은 주사위의 전개도를 어떻게 저장하고 이동할지 고민하였다. 풀이 과정을 나열하면 아래와 같다.

 1. K개의 명령어를 차례로 확인
 2. 지도 밖으로 나가는 경우는 continue
 3. 방향에 따른 주사위 전개도 인덱스 변경
 4. 이동칸이 0이 아닌 경우 주사위 바닥면으로 복사 후 이동칸은 0으로 변경
 5. 이동칸이 0인 경우 주사위 바닥면에 쓰여있는 숫자를 이동칸으로 복사

 

3번은 어렵게 생각할 필요없이 이동했을 때 저장한 주사위 눈금을 변경해주면 된다. 전개도는 1차원 배열로 저장하였다.

 조금 더 이해하기 쉽게끔 그림으로 정리해보았다. 초기 주사위를 [1, 2, 3, 4, 5, 6]으로 가정하면 각각의 움직였을 때의 인덱스는 위의 그림처럼 반환된다. 

2-2. 코드

 

#include <iostream>
#include <algorithm>
#include <queue>
#include <iterator>

using namespace std;


void MoveDice(pair<int, int> start, int N, int M);

const int MaxSize = 21;
const int DiceIdx = 6;
const int Dir = 4;

int map[MaxSize][MaxSize];
queue<int> directions;

int dirX[Dir] = { 0, 0, -1, 1 };
int dirY[Dir] = { 1, -1, 0, 0 };

int dice[DiceIdx] = { 0, 0, 0, 0, 0, 0 };
int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int N, M, x, y, K;
    cin >> N >> M >> x >> y >> K;

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            cin >> map[i][j];
        }
    }

    int num;

    while (K--) {
        cin >> num;
        directions.push(num);
    }

    MoveDice({x, y}, N, M); // 주사위 이동 함수
}

void MoveDice(pair<int, int> start, int N, int M) {
    pair<int, int> cur = start, next;
    int temp, dir;

    while (!directions.empty()) {

        dir = directions.front();
        directions.pop();

        next = { cur.first + dirX[dir - 1], cur.second + dirY[dir - 1] };
        if (next.first < 0 || next.first >= N || next.second < 0 || next.second >= M) continue;
        // 지도밖으로 나갔을 때
        
        int temp[DiceIdx];
        copy_n(dice, DiceIdx, temp);
        if (dir == 1) { // 동쪽
            int idx[DiceIdx] = { 2, 0, 5, 3, 4, 1 };
            for (int i = 0; i < DiceIdx; i++) dice[i] = temp[idx[i]];
        }
        else if (dir == 2) { // 서쪽
            int idx[DiceIdx] = { 1, 5, 0, 3, 4, 2 };
            for (int i = 0; i < DiceIdx; i++) dice[i] = temp[idx[i]];
        }
        else if (dir == 3) { // 북쪽
            int idx[DiceIdx] = { 4, 1, 2, 0, 5, 3 };
            for (int i = 0; i < DiceIdx; i++) dice[i] = temp[idx[i]];
        }
        else { // 남쪽
            int idx[DiceIdx] = { 3, 1, 2, 5, 0, 4 };
            for (int i = 0; i < DiceIdx; i++) dice[i] = temp[idx[i]];
        }

        if (map[next.first][next.second] == 0) map[next.first][next.second] = dice[5];
        else {
            dice[5] = map[next.first][next.second];
            map[next.first][next.second] = 0;
        }

        cout << dice[0] << "\n";

        cur = next;
    }
}