작심 24/7

[백준] 5373번 큐빙 본문

백준

[백준] 5373번 큐빙

모닝수박 2020. 7. 18. 00:07
 

5373번: 큐빙

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

www.acmicpc.net

내가 지금 시뮬레이션을 구현하고 있는 건지 노가다를 하고 있는 건지

잠시 현타(?) 왔던 문제이다.

 

그림처럼 한 면을 돌리면 그 면과 인접한 옆 면들도 돌려지기 때문에

돌리는 면과 옆 면

이 두 가지 경우로 나누어서 계산해주어야 한다.

 

돌리는 면에 따라 돌려져야 하는 칸들을 시계 방향으로 전개도에 표시해보았다.

 

1. 윗 면 (U)

2. 아랫 면 (D)

3. 앞 면 (F)

4. 뒷 면 (B)

5. 왼쪽 면 (L)

6. 오른쪽 면 (R)

기본 형태 : 1 → 2 → 3 → 4

시계 방향 : 4 1 → 2 → 3 

반시계 방향 :→ 3 → 4 → 1

 

이런 형태가 되므로

ScanCube 함수를 통해 이 두 경우를 기본 형태로 string에 담아주고

Change 함수를 통해 시계 방향 또는 반시계 방향으로 string을 변경해준 다음

MoveCube 함수를 통해 string 값을 기본 형태로 큐브 배열에 담아준다.

 

돌리는 면의 옆 면은 ScanCube 함수, MoveCube 함수, string 변수 line이고

인수는 각 화살표의 시작 좌표와 끝 좌표를 넣어주었다.

돌리는 면은 ScanCube2 함수, MoveCube2 함수, string 변수 line2이고

인수는 그 면의 시작 좌표 하나만 넣어주었다.

 

< 예전 버전 노가다 코드>

#include <iostream>
#include <string>
using namespace std;
int T, N;
char cube[13][10];
string tmp, tmp2, line, line2, order;
void InitCube() { //큐브 초기화
	for (int i = 0; i < 12; i++) {
		for (int j = 0; j < 9; j++) {
			if (i < 3 && j > 2 && j < 6) cube[i][j] = 'y';
			else if (i >= 3 && i < 6 && j > 2 && j < 6) cube[i][j] = 'o';
			else if (i >= 6 && i < 9 && j >= 0 && j < 3) cube[i][j] = 'g';
			else if (i >= 6 && i < 9 && j >= 3 && j < 6) cube[i][j] = 'w';
			else if (i >= 6 && i < 9 && j < 9) cube[i][j] = 'b';
			else if (i >= 9 && i < 12 && j>2 && j < 6) cube[i][j] = 'r';
			else cube[i][j] = '-';
		}
	}
}
void ScanCube(int from_x, int from_y, int to_x, int to_y) { //옆면을 시계 방향으로 스캔
	if (from_x == to_x) { //가로
		if (from_y < to_y) for (int i = from_y; i <= to_y; i++) line += cube[to_x][i]; //왼쪽에서 오른쪽으로
		else for (int i = from_y; i >= to_y; i--) line += cube[to_x][i]; //오른쪽에서 왼쪽으로
	}
	else { //세로
		if (from_x < to_x) for (int i = from_x; i <= to_x; i++) line += cube[i][to_y]; //위에서 아래로
		else for (int i = from_x; i >= to_x; i--) line += cube[i][to_y]; //아래에서 위로
	}
}
void ScanCube2(int x, int y) { //돌리는 면을 시계 방향으로 스캔
	for (int i = y; i < y + 3; i++) line2 += cube[x][i];
	for (int i = x; i < x + 3; i++) line2 += cube[i][y + 2];
	for (int i = y + 2; i >= y; i--) line2 += cube[x + 2][i];
	for (int i = x + 2; i >= x; i--) line2 += cube[i][y];
}
void Change(char ch) { //주어진 방향에 맞게 순서 배치
	if (ch == '+') {
		tmp = line.substr(9, 3), tmp2 = line2.substr(9, 3);
		tmp += line.substr(0, 9), tmp2 += line2.substr(0, 9);
		line = tmp, line2 = tmp2;
	}
	else {
		tmp = line.substr(3), tmp2 = line2.substr(3);
		tmp += line.substr(0, 3), tmp2 += line2.substr(0, 3);
		line = tmp, line2 = tmp2;
	}
}
void MoveCube(int from_x, int from_y, int to_x, int to_y, int idx) { //바뀐 순서를 옆면에 시계 방향으로 집어 넣음
	if (from_x == to_x) { //가로
		if (from_y < to_y) for (int i = from_y; i <= to_y; i++) cube[to_x][i] = line[idx++]; //왼쪽에서 오른쪽으로
		else for (int i = from_y; i >= to_y; i--) cube[to_x][i] = line[idx++]; //오른쪽에서 왼쪽으로
	}
	else { //세로
		if (from_x < to_x) for (int i = from_x; i <= to_x; i++) cube[i][to_y] = line[idx++]; //위에서 아래로
		else for (int i = from_x; i >= to_x; i--) cube[i][to_y] = line[idx++]; //아래에서 위로
	}
}
void MoveCube2(int x, int y) { //바뀐 순서를 돌리는 면에 시계 방향으로 집어 넣음
	int idx = 0;
	for (int i = y; i < y + 3; i++) cube[x][i] = line2[idx++];
	for (int i = x; i < x + 3; i++) cube[i][y + 2] = line2[idx++];;
	for (int i = y + 2; i >= y; i--) cube[x + 2][i] = line2[idx++];;
	for (int i = x + 2; i >= x; i--) cube[i][y] = line2[idx++];;
}
int main() {
	cin >> T;
	for (int t = 0; t < T; t++) {
		cin >> N;
		InitCube();
		for (int n = 0; n < N; n++) {
			cin >> order;
			switch (order[0])
			{
			case 'U':
				ScanCube(5, 3, 5, 5), ScanCube(6, 6, 8, 6), ScanCube(9, 5, 9, 3), ScanCube(8, 2, 6, 2), ScanCube2(6, 3);
				Change(order[1]);
				MoveCube(5, 3, 5, 5, 0), MoveCube(6, 6, 8, 6, 3), MoveCube(9, 5, 9, 3, 6), MoveCube(8, 2, 6, 2, 9), MoveCube2(6, 3);
				break;
			case 'D':
				ScanCube(11, 3, 11, 5), ScanCube(8, 8, 6, 8), ScanCube(3, 5, 3, 3), ScanCube(6, 0, 8, 0), ScanCube2(0, 3);
				Change(order[1]);
				MoveCube(11, 3, 11, 5, 0), MoveCube(8, 8, 6, 8, 3), MoveCube(3, 5, 3, 3, 6), MoveCube(6, 0, 8, 0, 9), MoveCube2(0, 3);
				break;
			case 'F':
				ScanCube(8, 3, 8, 5), ScanCube(8, 6, 8, 8), ScanCube(0, 5, 0, 3), ScanCube(8, 0, 8, 2), ScanCube2(9, 3);
				Change(order[1]);
				MoveCube(8, 3, 8, 5, 0), MoveCube(8, 6, 8, 8, 3), MoveCube(0, 5, 0, 3, 6), MoveCube(8, 0, 8, 2, 9), MoveCube2(9, 3);
				break;
			case 'B':
				ScanCube(2, 3, 2, 5), ScanCube(6, 8, 6, 6), ScanCube(6, 5, 6, 3), ScanCube(6, 2, 6, 0), ScanCube2(3, 3);
				Change(order[1]);
				MoveCube(2, 3, 2, 5, 0), MoveCube(6, 8, 6, 6, 3), MoveCube(6, 5, 6, 3, 6), MoveCube(6, 2, 6, 0, 9), MoveCube2(3, 3);
				break;
			case 'L':
				ScanCube(3, 3, 5, 3), ScanCube(6, 3, 8, 3), ScanCube(9, 3, 11, 3), ScanCube(0, 3, 2, 3), ScanCube2(6, 0);
				Change(order[1]);
				MoveCube(3, 3, 5, 3, 0), MoveCube(6, 3, 8, 3, 3), MoveCube(9, 3, 11, 3, 6), MoveCube(0, 3, 2, 3, 9), MoveCube2(6, 0);
				break;
			case 'R':
				ScanCube(5, 5, 3, 5), ScanCube(2, 5, 0, 5), ScanCube(11, 5, 9, 5), ScanCube(8, 5, 6, 5), ScanCube2(6, 6);
				Change(order[1]);
				MoveCube(5, 5, 3, 5, 0), MoveCube(2, 5, 0, 5, 3), MoveCube(11, 5, 9, 5, 6), MoveCube(8, 5, 6, 5, 9), MoveCube2(6, 6);
				break;
			}
			line = line2 = tmp = tmp2 = "";
		}
		for (int i = 6; i <= 8; i++) {
			for (int j = 3; j <= 5; j++) cout << cube[i][j];
			cout << "\n";
		}
	}
	return 0;
}

 

( + 풀이 추가 2021.04.21)

 

1. cube배열을 6종류의 3 X 3 배열로 만들기

 

2. 중앙 면을 기준으로 바뀌는 옆면들을 order배열에 저장해두기

 

기본 로직 : 

num면의, idx번째, 행or열을, 그대로or거꾸로 복사해서

num면의, idx번째, 행or열에, 그대로 집어넣는다

 

그냥 전개도 보면서 옆면들이 어떻게 바뀌는지 열심히 연구해서

이차원 배열에 때려 박고 함수 하나로 돌려썼다.

 

3. 시계 방향으로 3번 돌리면 반시계 방향이라는 사실을 이용하기


< 최신 버전 덜 노가다 코드>

#include <iostream>
using namespace std;
int T, N;
int c[300]; // 알파벳에 맞는 숫자 집어넣기용
int order[6][16] = { // num의, idx번째, 행 or 열(0 or 1)을, 그대로 or 거꾸로(0 or 1) => 이 순서대로 4번씩
					 {5,0,0,0,4,0,0,0,3,0,0,0,2,0,0,0},{3,2,0,0,4,2,0,0,5,2,0,0,2,2,0,0},
					 {0,0,1,0,3,0,1,0,1,0,1,1,5,2,1,1},{0,2,0,0,4,0,1,1,1,0,0,0,2,2,1,1},
					 {0,2,1,1,5,0,1,1,1,2,1,0,3,2,1,0},{0,0,0,1,2,0,1,0,1,2,0,1,4,2,1,0}
					};
char cube[6][3][3]; // 3X3씩 6면 - U,D,L,F,R,B
char color[6] = { 'w', 'y', 'g', 'r', 'b', 'o' };

void rotate(int c) {
	char tmp[4][3] = { 0 };
	for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) tmp[i][j] = cube[c][i][j]; // 중앙 면 복사
	for (int i = 0, m = 2; i < 3; i++, m--) for (int j = 0; j < 3; j++) cube[c][j][m] = tmp[i][j]; // 중앙 면에 복사한 거 넣기

	for (int t = 0; t < 16; t += 4) { // 옆 면 복사
		int num = order[c][t], idx = order[c][t + 1], col = order[c][t + 2], reverse = order[c][t + 3]; // num의 idx번째 행or열(col)을 그대로or거꾸로(reverse)
		if (col) { // 열을 읽을 때
			if (reverse) for (int i = 2; i >= 0; i--) tmp[t / 4][2 - i] = cube[num][i][idx]; // 거꾸로 읽을 때
			else for (int i = 0; i < 3; i++) tmp[t / 4][i] = cube[num][i][idx]; // 그대로 읽을 때
		}
		else { // 행을 읽을 때
			if (reverse) for (int i = 2; i >= 0; i--) tmp[t / 4][2 - i] = cube[num][idx][i]; // 거꾸로 읽을 때
			else for (int i = 0; i < 3; i++) tmp[t / 4][i] = cube[num][idx][i]; // 그대로 읽을 때
		}
	}
	int tidx = 0;
	for (int t = 4; t < 20; t += 4) { // 옆 면에 복사한 거 넣기
		int num = order[c][t % 16], idx = order[c][(t + 1) % 16], col = order[c][(t + 2) % 16]; // num의 idx번째 행or열(col)
		if (col) { // 열에 넣을 때
			for (int i = 0; i < 3; i++) cube[num][i][idx] = tmp[tidx][i];
		}
		else { // 행에 넣을 때
			for (int i = 0; i < 3; i++) cube[num][idx][i] = tmp[tidx][i];
		}
		tidx++;
	}
}

int main() {
	c['U'] = 0, c['D'] = 1, c['L'] = 2, c['F'] = 3, c['R'] = 4, c['B'] = 5;
	cin >> T;
	for (int t = 1; t <= T; t++) {
		for (int k = 0; k < 6; k++) for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) cube[k][i][j] = color[k]; // 큐브 초기화
		cin >> N;
		string input;
		for (int i = 0; i < N; i++) {
			cin >> input;
			if (input[1] == '-') for (int j = 0; j < 3; j++) rotate(c[input[0]]); // 시계 방향 3번 == 반시계 방향
			else rotate(c[input[0]]);
		}
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) cout << cube[0][i][j];
			cout << "\n";
		}
	}
	return 0;
}

'백준' 카테고리의 다른 글

[백준] 17143번 낚시왕  (0) 2020.07.20
[백준] 16235번 나무 재테크  (0) 2020.07.19
[백준] 16236번 아기 상어 (C++, JAVA)  (0) 2020.07.16
[백준] 12100번 2048 (Easy)  (0) 2020.07.16
[백준] 2096번 내려가기  (0) 2020.07.13
Comments