백준 14499번: 주사위 굴리기

1) 문제 분석

  주사위에 대한 정보를 어떻게 표현할지가 문제의 관건이다. 즉, 주사위 각 면에 적힌 숫자와 주사위가 각각의 방향으로 굴렀을 때 어떤 면이 위로 오는 지를 알 수 있으면 나머지는 코드를 작성하기 매우 쉬워진다. 따라서 우선 주사위를 각 방향으로 굴렸을 때 주사위 각 면이 어떤 숫자가 되는지 정하는 함수를 작성하기로 하였다.

2) 알고리즘

  주사위를 전개도로 펼쳤을 때, 주사위의 윗면과 윗면의 위에 위치한 면, 윗면의 오른쪽의 위치한 면이 각각 어떤 면인지를 TOP, UP, RGHT 이라는 전역 변수에 저장하였다. 세 면의 반대편 면들은 7에서 그 값을 빼주면 된다. 즉, 면2의 반대편에는 면5가 있을 것이다.   그리고 주사위가 각 방향으로 굴러 갈 때 마다 이 값들을 조정해주는 roll_dice라는 함수를 작성하였다. 이 함수는 굴러가는 방향을 인자로 받아서 각각에 대해 알맞게 처리한다. 예를 들어, TOP=1, UP=2, RGHT=3 이고 주사위가 동쪽으로 굴러간다고 하자. 그러면 TOP은 원래 RGHT의 반대편 면이 될 것이고(TOP=7-3=4), UP은 변하지 않을 것이며(UP=2) RGHT은 원래의 탑이 된다(RGHT=1). 마찬가지로 나머지 방향에 대해서도 알맞게 변수들의 값을 바꿔주면 된다. 이제 주사위가 굴러가는 방향을 입력받아서 우선 그 방향으로 가면 범위에서 벗어나지 않는지 체크하고, 그렇지 않다면 문제의 조건에 맞게 주사위 바닥면에 쓰인 값과 주사위가 놓인 칸의 값을 변경해주고 주사위 윗면의 값을 출력해주면 된다.

3) 코드

#include <iostream>

using namespace std;

//opposite face
#define opposite(n) (7-n)
//directions(East, West, North, South)
enum dir{E=1, W=2, N=3, S=4};
//TOP: upper surface, UP: face on the upper side of TOP, RGHT: face on the right side of TOP
int TOP, UP, RGHT;
//value of each surface
int dice[7] = {0};

//changes in the position of the dice according to the rolling direction
int dir_x[] = {0,0,0,-1,1};
int dir_y[] = {0,1,-1,0,0};

/**
 * check if the coordinates are within the range
 * @param (x,y): (row,col)
 * @param n: rows
 * @param m: cols
 * @return true if (x,y) is within the range, false if not
 */
bool isInRange(int x, int y, int n, int m){
    return (0<=x && x<n && 0<=y && y<m);
}

/**
 * roll the dice towards the direction of d
 * and change the values of TOP, UP, RGHT accordingly
 * @param d: direction to which the dice rolls
 */
void roll_dice(int d){
    int temp;
    switch(d){
        case E:  //to East
            temp = RGHT; RGHT =TOP; TOP = opposite(temp);
            break;
        case W:  //to West
            temp = RGHT; RGHT = opposite(TOP); TOP = temp;
            break;
        case N:  //to North
            temp = UP; UP = TOP; TOP = opposite(temp);
            break;
        case S:  //to South
            temp = UP; UP = opposite(TOP); TOP = temp;
        break;
        default: break;
    }
}

int main(){
    int N, M;  //number of rows(N) and columns(M) of the board
    int x, y;  //current position of the dice
    int k;     //number of rolls
    int board[20][20];
    int rolls[1000];

    ios_base::sync_with_stdio(false);

    //get input from user
    cin >> N; cin >> M;
    cin >> x; cin >> y;
    cin >> k;

    //each block of board has a value ranges from 0 to 10
    for(int i=0; i<N; i++){
        for(int j=0; j<M; j++)
            cin >> board[i][j];
    }
    //save the directions
    for(int i=0; i<k; i++)
        cin >> rolls[i];

    //upper surface is 1
    TOP = 1; UP = 2; RGHT = 3;
    //for each roll
    for(int i=0; i<k; i++) {
        int roll = rolls[i];  //get direction
        //range check
        if(!isInRange(x+dir_x[roll], y+dir_y[roll],N,M))
            continue;
        //update the current position of the dice
        x += dir_x[roll]; y += dir_y[roll];
        //update the upper surface and rest of the faces
        roll_dice(roll);

        if(!board[x][y]) //value of (x,y) is 0
            //copy the value of the bottom surface to (x,y)
            board[x][y] = dice[opposite(TOP)];
        else{   //value of (x,y) is not 0
            //copy the value to the bottom surface
            dice[opposite(TOP)] = board[x][y];
            //set the value of (x,y) to 0
            board[x][y] = 0;
        }
        //print out the value of upper surface
        cout << dice[TOP] << "\n";
    }

}