백준 5373 큐빙

최재원·2022년 8월 27일
0

알고리즘

목록 보기
13/13

문제


5373번: 큐빙 (acmicpc.net)

해결방법


  • 복잡한 구현문제다. 큐브의 각 면과 해당 면의 9가지 칸들을 잘 정리해서 생각해야한다.

  • 큐브가 이렇게 있고

  • 각 면의 칸의 index는 이렇게 설정했다. ( 실제론 0~8 이므로 1씩 빼줘야함 )

  • 아랫면만 반대로 한 이유는 어쩌다 보니 이렇게 하는게 더 편했다.

  • 이렇게 해서 각각의 회전을 처리하면 된다.

    • 예를 들어 오른쪽을 시계 방향으로 돌리면 앞면의 3, 6, 9가 윗면의 3, 6, 9가 되고 윗면의 3, 6, 9가 뒷면의 3, 6, 9가 되고 뒷면의 3, 6, 9가 아랫면의 9, 6, 3이 된고 아랫면의 9, 6, 3이 앞면의 3, 6, 9가 된다. 그리고 오른쪽 내부의 칸을 오른쪽으로 90도 돌려주면 된다.
    • 윗면을 반 시계 방향으로 돌리면 앞면의 1, 2, 3이 왼쪽면의 3, 6, 9가 되고 그건 뒷면의 7, 8, 9가 되고 그건 오른쪽면의 1, 4, 7이 된다. 그리고 윗면의 내용을 왼쪽으로 90도 돌리면 된다.
  • 즉 큐브를 한번 돌리면 해당 면을 돌릴 때 돌아갈 4개의 면의 칸3개가 정해져있고 이 칸들을 방향에 맞게 돌리고, 돌리는 해당 면의 내부 칸들을 방향에 맞게 90도 돌리면 된다.

  • 이 때 아랫면은 반대로 했기 때문에 아랫면의 내부를 돌릴 땐 반대로 돌린다.

동작과정


  1. 초기 큐브를 설정하고 명령어를 입력받는다.
  2. 명령어를 순서대로 입력받으며 큐브를 돌리는 함수를 수행한다.
    • 특정 면을 돌릴 때 돌아갈 면과 칸들은 정해져 있고 그걸 다 적어놧다. 그걸 시계방향 혹은 반시계 방향으로 돌린다.
    • 정해진 면과 칸들을 관리하기 위해 SelectedLine 클래스를 만들어 사용했다.
    • 정해진 면의 칸의 내용들을 미리 tmp에 넣어 놓고 방향에 맞게 돌려서 cube에 넣는다.
  3. 모든 회전을 처리 한 뒤 큐브 맨 위칸을 출력한다.

코드


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;

public class Main {

	/* 회전에서 돌아갈 줄의 내용을 담고 있는 클래스
	 * position : 돌아갈 면의 위치
	 * line : 해당 면에서 돌아갈 칸의 위치 3개
	 */
	private static class SelectedLine {
		int position;
		int[] line;

		public SelectedLine(int position, int[] line) {
			this.position = position;
			this.line = line;
		}

	}

	/*
	 *  복잡한 구현 문제이다.
	 *  회전이 일어날 때 정해진 면의 칸들을 방향에 맞게 이동시키고 회전하는 면의 모든 칸을 방향에 따라 회전시킨다.
	 */
	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));

		int T = Integer.parseInt(in.readLine());

		for (int t = 0; t < T; t++) {
			int n = Integer.parseInt(in.readLine());
			StringTokenizer st = new StringTokenizer(in.readLine());

			// 큐브의 초기 값들 순서대로 위, 아래, 앞, 뒤, 왼쪽, 오른쪽
			char[][] cube = { { 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w', 'w' }, // U
					{ 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y' }, // D
					{ 'r', 'r', 'r', 'r', 'r', 'r', 'r', 'r', 'r' }, // F
					{ 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o', 'o' }, // B
					{ 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g' }, // L
					{ 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b' } // R
			};

			// 들어오는 명령어에 맞게 회전하는 함수와 해당 면을 돌리는 함수를 실행한다.
			for (int i = 0; i < n; i++) {

				String rotaion = st.nextToken();

				char p = rotaion.charAt(0);
				boolean clock = rotaion.charAt(1) == '+';
				
				switch (p) {
				case 'U':
					U(clock, cube);
					break;
				case 'D':
					D(clock, cube);
					break;
				case 'F':
					F(clock, cube);
					break;
				case 'B':
					B(clock, cube);
					break;
				case 'L':
					L(clock, cube);
					break;
				case 'R':
					R(clock, cube);
					break;
				}
				
				lotateSquare(cube, p, clock);
				
			}
			
			// 모든 명령어를 수행한 후 윗면의 내용을 출력한다.
			for (int i = 0; i < 9; i++) {
				out.write(cube[0][i]);
				if ((i + 1) % 3 == 0)
					out.write("\n");
			}
		}

		out.flush();
	}

	// 윗면을 돌릴때 앞, 오른쪽, 뒤, 왼쪽의 위쪽 칸을 돌린다.
	private static void U(boolean clock, char[][] cube) {
		// 앞, 오른쪽, 뒤, 왼쪽

		// 각면의 위쪽에 해당하는 칸의 index를 가져온다.
		SelectedLine[] lines = new SelectedLine[4];
		lines[0] = new SelectedLine(2, new int[] {0, 1, 2});
		lines[1] = new SelectedLine(5, new int[] {6, 3, 0});
		lines[2] = new SelectedLine(3, new int[] {8, 7, 6});
		lines[3] = new SelectedLine(4, new int[] {2, 5, 8});

		rotation(cube, lines, clock);
	}
	
	// 아랫면을 돌릴때 앞, 오른쪽, 뒤, 왼쪽의 위쪽 칸을 돌린다.
	private static void D(boolean clock, char[][] cube) {
		// 앞, 왼쪽, 뒤, 오른쪽

		// 각면의 아래쪽에 해당하는 칸의 index를 가져온다.
		SelectedLine[] lines = new SelectedLine[4];
		lines[0] = new SelectedLine(2, new int[] {6, 7, 8});
		lines[1] = new SelectedLine(4, new int[] {0, 3, 6});
		lines[2] = new SelectedLine(3, new int[] {2, 1, 0});
		lines[3] = new SelectedLine(5, new int[] {8, 5, 2});

		rotation(cube, lines, clock);
	}

	// 앞면을 돌릴때 위, 왼쪽, 아래, 오른쪽의 앞쪽 칸을 돌린다.
	private static void F(boolean clock, char[][] cube) {
		// 위, 왼쪽, 아래, 오른쪽

		// 각면의 앞쪽에 해당하는 칸의 index를 가져온다.
		SelectedLine[] lines = new SelectedLine[4];
		lines[0] = new SelectedLine(0, new int[] {6, 7, 8});
		lines[1] = new SelectedLine(4, new int[] {6, 7, 8});
		lines[2] = new SelectedLine(1, new int[] {8, 7, 6});
		lines[3] = new SelectedLine(5, new int[] {6, 7, 8});

		rotation(cube, lines, clock);
	}
	
	// 뒷면을 돌릴때 위, 왼쪽, 아래, 오른쪽의 뒤쪽 칸을 돌린다.
	private static void B(boolean clock, char[][] cube) {
		// 위, 오른쪽, 아래, 왼쪽

		// 각면의 뒷쪽에 해당하는 칸의 index를 가져온다.
		SelectedLine[] lines = new SelectedLine[4];
		lines[0] = new SelectedLine(0, new int[] {0, 1, 2});
		lines[1] = new SelectedLine(5, new int[] {0, 1, 2});
		lines[2] = new SelectedLine(1, new int[] {2, 1, 0});
		lines[3] = new SelectedLine(4, new int[] {0, 1, 2});

		rotation(cube, lines, clock);
	}

	// 오른쪽면을 돌릴때 앞, 아래, 뒤, 위의 오른쪽 칸을 돌린다.
	private static void R(boolean clock, char[][] cube) {
		// 앞, 아래, 뒤, 위

		// 각면의 오른쪽에 해당하는 칸의 index를 가져온다.
		SelectedLine[] lines = new SelectedLine[4];
		lines[0] = new SelectedLine(2, new int[] {2, 5, 8});
		lines[1] = new SelectedLine(1, new int[] {8, 5, 2});
		lines[2] = new SelectedLine(3, new int[] {2, 5, 8});
		lines[3] = new SelectedLine(0, new int[] {2, 5, 8});

		rotation(cube, lines, clock);
	}
	
	// 왼쪽면을 돌릴때 앞, 아래, 뒤, 위의 오른쪽 칸을 돌린다.
	private static void L(boolean clock, char[][] cube) {
		// 앞, 위, 뒤, 아래

		// 각면의 왼쪽에 해당하는 칸의 index를 가져온다.
		SelectedLine[] lines = new SelectedLine[4];
		lines[0] = new SelectedLine(2, new int[] {0, 3, 6});
		lines[1] = new SelectedLine(0, new int[] {0, 3, 6});
		lines[2] = new SelectedLine(3, new int[] {0, 3, 6});
		lines[3] = new SelectedLine(1, new int[] {6, 3, 0});

		rotation(cube, lines, clock);
	}

	// 칸들을 돌려 cube에 저장하는 함수
	private static void rotation(char[][] cube, SelectedLine[] lines, boolean clock) {
		char[][] tmp = new char[4][];

		// 시계방향이면 다음 순서의 내용을 이전 lines에 넣는다.
		// 넣기전 tmp 에 값들을 순서대로 저쟁해놓음
		if (clock) {
			for (int i = 0; i < 3; i++) {
				tmp[i] = getLine(cube, lines[i + 1]);
			}
			tmp[3] = getLine(cube, lines[0]);
		}

		else {
			for (int i = 1; i < 4; i++) {
				tmp[i] = getLine(cube, lines[i - 1]);
			}
			tmp[0] = getLine(cube, lines[3]);
		}

		// 저장된 값을 cube에 적용
		for (int i = 0; i < 4; i++) {
			setLine(cube, lines[i], tmp[i]);
		}
	}

	// selectedLine에 존재하는 면과 칸들의 index 정보를 토대로 실제 값들을 가진 result를 리턴한다.
	private static char[] getLine(char[][] cube, SelectedLine selectedLine) {
		char[] result = new char[3];

		int p = selectedLine.position;
		int[] line = selectedLine.line;

		for(int i = 0; i < 3; i++)
			result[i] = cube[p][line[i]];

		return result;
	}

	// selectedLine에 존재하는 면과 칸들의 index 위치에 tmp에 해당하는 값을 넣어준다.
	private static void setLine(char[][] cube, SelectedLine selectedLine, char[] tmp) {

		int p = selectedLine.position;
		int[] line = selectedLine.line;
		
		for(int i = 0; i < 3; i++)
			cube[p][line[i]] = tmp[i];
	}
	
	// 해당 면을 방향대로 돌리는 함수, 단 아랫면은 반대로 돌린다 -> 아랫면은 오른쪽 왼쪽을 바꾸어서 사용했기 때문
	private static void lotateSquare(char[][] cube, char p, boolean clock) {
		char[] tmp = new char[9];
		
		int position = -1;
		switch (p) {
		case 'U':
			position = 0;
			break;
		case 'D':
			position = 1;
			clock = !clock;
			break;
		case 'F':
			position = 2;
			break;
		case 'B':
			position = 3;
			break;
		case 'L':
			position = 4;
			break;
		case 'R':
			position = 5;
			break;
		}
		
		if(!clock) {
			int cnt = 0;
			for(int i = 2; i >= 0; i--) {
				for(int j = 0; j < 3; j++) {
					tmp[cnt++] = cube[position][i+ (j*3)];
				}
			}
		}
		
		else {
			int cnt = 0;
			for(int i = 6; i <= 8; i++) {
				for(int j = 0; j < 3; j++) {
					tmp[cnt++] = cube[position][i - (j*3)];
				}
			}
		}
		
		cube[position] = tmp;
	}
}
profile
성장 중

0개의 댓글