(백준 5373번) 큐빙

문제 설명


루빅스 큐브는 삼차원 퍼즐이다. 보통 루빅스 큐브는 3×3×3개의 작은 정육면체로 이루어져 있다. 퍼즐을 풀려면 각 면에 있는 아홉 개의 작은 정육면체의 색이 동일해야 한다.

큐브는 각 면을 양방향으로 90도 만큼 돌릴 수 있도록 만들어져 있다. 회전이 마친 이후에는, 다른 면을 돌릴 수 있다. 이렇게 큐브의 서로 다른 면을 돌리다 보면, 색을 섞을 수 있다.

이 문제에서는 루빅스 큐브가 모두 풀린 상태에서 시작한다. 윗 면은 흰색, 아랫 면은 노란색, 앞 면은 빨간색, 뒷 면은 오렌지색, 왼쪽 면은 초록색, 오른쪽 면은 파란색이다.

루빅스 큐브를 돌린 방법이 순서대로 주어진다. 이때, 모두 돌린 다음에 가장 윗 면의 색상을 구하는 프로그램을 작성하시오.

img

위의 그림은 루빅스 큐브를 푼 그림이다. 왼쪽 면은 시계방향으로 조금 돌려져 있는 상태이다.

입력


첫째 줄에 테스트 케이스의 개수가 주어진다. 테스트 케이스는 최대 100개이다. 각 테스트 케이스는 다음과 같이 구성되어져 있다.

  • 첫째 줄에 큐브를 돌린 횟수 n이 주어진다. (1 ≤ n ≤ 1000)
  • 둘째 줄에는 큐브를 돌린 방법이 주어진다. 각 방법은 공백으로 구분되어져 있으며, 첫 번째 문자는 돌린 면이다. U: 윗 면, D: 아랫 면, F: 앞 면, B: 뒷 면, L: 왼쪽 면, R: 오른쪽 면이다. 두 번째 문자는 돌린 방향이다. +인 경우에는 시계 방향 (그 면을 바라봤을 때가 기준), -인 경우에는 반시계 방향이다.

출력


각 테스트 케이스에 대해서 큐브를 모두 돌린 후의 윗 면의 색상을 출력한다. 첫 번째 줄에는 뒷 면과 접하는 칸의 색을 출력하고, 두 번째, 세 번째 줄은 순서대로 출력하면 된다. 흰색은 w, 노란색은 y, 빨간색은 r, 오렌지색은 o, 초록색은 g, 파란색은 b.

문제 풀이


사람의 공간지각력과 정확한 인덱싱을 한계까지 시험하는듯한 문제였다. 문제 자체는 상당히 간단하다. 큐브라고 해봤자 9개짜리 면 6개로 54개밖에 안되고 이걸 시계 반시계로 빙빙 돌리면 되는 문제이다.
가장 큰 문제는 한 면을 돌리면 인접한 면이 따라서 빙빙 돌아간다는 것이다. 따라서 한 면에 접하고 있는 4 면이 각각 무엇인지에 대한 자료구조가 필요했고, 나는 거기에 더해서 이 4 면에서 회전하는 부분의 인덱스를 자료구조에 같이 넣어 회전할 때의 연산을 용이하게 하기로 했다. 따라서 0번에 면의 번호, 1 2 3번에 그 면에서 회전하는 부분의 인덱스, 한 면을 회전시키면 4면이 돌아가므로 그러한 0123번이 *4개 , 큐브는 6개의 면이 있으므로 *6개인 3차원 배열이 생성되었다.
배열을 만들 때 시계방향으로 회전 시의 경우를 고려하여 만들었기 때문에 회전시킬 때에 자료구조의 인덱스대로 이전 인덱스의 값을 다음 인덱스에 넣으면 회전이 완료되도록 구성하였다. 반시계 회전일땐 앞의 인덱스의 값을 이전 인덱스에 넣으면 된다. 배열을 만든 후에는 입력받은대로 큐브를 회전시키면 된다.
이 문제가 어려운 이유 중 하나는 인덱스를 헷갈리기가 정말 쉽다는 것이다. 옆에 큐브를 두고 큐브의 면마다 인덱스를 적어서 푸는 것이 아닌 이상 머리 속에서만 큐브를 굴리면 정말 헷갈린다. 예를 들어 앞면의 인덱스를
1 2 3
4 5 6
7 8 9
라고 했다고 치자. 여기까지는 쉽다. 그렇다면 윗면과 아랫면은 어떻게 인덱싱할 것인가? 윗면은 앞면과 똑같이 인덱싱할 수 있지만 그 반대편인 아랫면은 어떤 기준으로 인덱싱 할 지가 난해해진다. 윗면에서 봤을 때 뒷면을 투영하는 식으로 인덱싱 할 것인가, 아니면 윗면을 뒤집어 놓은 것과 같이 인덱싱 할 것인가? 이러한 문제가 6면을 인덱싱 하다보면 전에 자기가 어떤 식으로 했는지 헷갈리기 시작하고 오답이 나기 쉬워진다. 이 문제를 풀면서 공간을 인덱싱할 때에는 명확한 기준을 두고 인덱싱을 해야 오답이 나지 않는다는 것을 배울 수 있었다. 풀어보고나니 난이도가 플래티넘5라 좀 놀랐다. 플래티넘 난이도 문제는 처음 풀어봤는데 좀 뿌듯했다. 자세한 코드는 다음과 같다.

코드


#include <iostream>
#include <bits/stdc++.h>
using namespace std;

char cube[6][9];
vector<vector<int>> v[6];//인접하는 면의 자료구조

void spin(char a, char b) {
    int dim; bool clock=false;
    if(a=='U') dim=0;
    else if(a=='D') dim=1;
    else if(a=='F') dim=2;
    else if(a=='B') dim=3;
    else if(a=='L') dim=4;
    else if(a=='R') dim=5;
    if(b=='+') clock=true;
    if(clock) {
        int tmp[9];
        tmp[2]=cube[dim][0]; tmp[5]=cube[dim][1]; tmp[8]=cube[dim][2];
        tmp[1]=cube[dim][3]; tmp[4]=cube[dim][4]; tmp[7]=cube[dim][5];
        tmp[0]=cube[dim][6]; tmp[3]=cube[dim][7]; tmp[6]=cube[dim][8];
        for(int i=0; i<9; i++) {
            cube[dim][i]=tmp[i];
        }//일단 주어진 면을 회전시킨다
        vector<vector<int>> arr=v[dim];
        char a1,a2,a3,b1,b2,b3;
        int idx1,idx2,idx3;//주어진 면과 접하는 면의 자료구조를 가져옴
        dim=arr[0][0]; idx1=arr[0][1]; idx2=arr[0][2]; idx3=arr[0][3];
        b1=cube[dim][idx1]; b2=cube[dim][idx2]; b3=cube[dim][idx3];
        for(int i=1; i<=4; i++) {
            if(i==4) i=0;
            dim=arr[i][0]; idx1=arr[i][1]; idx2=arr[i][2]; idx3=arr[i][3];
            a1=cube[dim][idx1]; a2=cube[dim][idx2]; a3=cube[dim][idx3];
            cube[dim][idx1]=b1; cube[dim][idx2]=b2; cube[dim][idx3]=b3;
            b1=a1; b2=a2; b3=a3;
            if(i==0) break;
        }//자료구조의 이전 인덱스의 값을 다음 인덱스의 면에 입력시킴
    }
    else {
        int tmp[9];
        tmp[6]=cube[dim][0]; tmp[3]=cube[dim][1]; tmp[0]=cube[dim][2];
        tmp[7]=cube[dim][3]; tmp[4]=cube[dim][4]; tmp[1]=cube[dim][5];
        tmp[8]=cube[dim][6]; tmp[5]=cube[dim][7]; tmp[2]=cube[dim][8];
        for(int i=0; i<9; i++) {
            cube[dim][i]=tmp[i];
        }
        vector<vector<int>> arr=v[dim];
        char a1,a2,a3,b1,b2,b3;
        int idx1,idx2,idx3;
        dim=arr[0][0]; idx1=arr[0][1]; idx2=arr[0][2]; idx3=arr[0][3];
        b1=cube[dim][idx1]; b2=cube[dim][idx2]; b3=cube[dim][idx3];
        for(int i=3; i>=0; i--) {
            dim=arr[i][0]; idx1=arr[i][1]; idx2=arr[i][2]; idx3=arr[i][3];
            a1=cube[dim][idx1]; a2=cube[dim][idx2]; a3=cube[dim][idx3];
            cube[dim][idx1]=b1; cube[dim][idx2]=b2; cube[dim][idx3]=b3;
            b1=a1; b2=a2; b3=a3;
        }
    }시계방향 회전과 방향이 다를  원리는 같다
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(0);
    int t; cin >> t;
    v[0]={ {2,0,1,2},{4,0,1,2},{3,0,1,2},{5,0,1,2} };
    v[1]={ {2,6,7,8},{5,6,7,8},{3,6,7,8},{4,6,7,8} };
    v[2]={ {0,6,7,8},{5,0,3,6},{1,2,1,0},{4,8,5,2} };
    v[3]={ {0,2,1,0},{4,0,3,6},{1,6,7,8},{5,8,5,2} };
    v[4]={ {0,0,3,6},{2,0,3,6},{1,0,3,6},{3,8,5,2} };
    v[5]={ {0,8,5,2},{3,0,3,6},{1,8,5,2},{2,8,5,2} };
    //각 면과 접하는 면들, 그리고 접하는 면들에서 접하는 부분의 인덱스를  
    //시계방향 회전의 경우를 기준으로 순서대로 입력해줌
    for(int i=0; i<t; i++) {
        int n; cin >> n;
        for(int j=0; j<9; j++) {
            cube[0][j]='w';
        }
        for(int j=0; j<9; j++) {
            cube[1][j]='y';
        }
        for(int j=0; j<9; j++) {
            cube[2][j]='r';
        }
        for(int j=0; j<9; j++) {
            cube[3][j]='o';
        }
        for(int j=0; j<9; j++) {
            cube[4][j]='g';
        }
        for(int j=0; j<9; j++) {
            cube[5][j]='b';
        }//큐브 초기화
        for(int j=0; j<n; j++) {
            char a, b; cin >> a >> b;
            spin(a,b);
        }//입력받은대로 회전
        for(int j=0; j<9; j++) {
            if(j%3==0 && j!=0) cout << "\n";
            cout << cube[0][j];
        }//회전 결과 출력
        cout << "\n";
    }   
}