APS

[JAVA] BOJ_14499. 주사위 굴리기

코릴라입니다 2023. 11. 4. 23:03

🦍 문제

 

14499번: 주사위 굴리기

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x, y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다. 둘째 줄부터 N개의 줄에 지

www.acmicpc.net

 


🐈 문제 풀이 

1. 무엇을 구해야 할까?

 - 입력되는 명령에 따라 주사위를 굴리며 이동시켜야 하고, 그 때마다 주사위의 윗면에 적혀져 있는 숫자를 출력해야한다.

2. 어떻게 구해야 할까?

1) 주사위의 각 면에 적혀져 있는 숫자들을 기록하기 위해 배열을 사용하였다.

	// 상 : 주사위의 윗면, 하 : 주사위의 바닥면
	// lrud[0] : {상, 서, 하, 동} - 동서 방향으로 이동시 사용
	// lrud[1] : {상, 북, 하, 남} - 남북 방향으로 이동시 사용
	static int[][] lrud = new int[][] { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };

 - lrud 배열을 통해 주사위 상태 관리

 - lrud[0] : 동-서 방향으로 주사위 굴릴 시 값 갱신

 - lrud[1] : 남-북 방향으로 주사위 굴릴 시 값 갱신

 - 주사위의 윗면, 바닥면(상, 하)은 어떤 방향으로 굴리던지 값이 갱신되므로, 이 부분에 해당되는 값은 lrud[0]과 lrud[1]에서 항상 동일하게끔 유지시켰다.

 

2) 주사위를 굴리는 방향에 따라 해당 배열을 조작하는 방식을 달리하여 메서드로 작성하였다.

 - 동쪽과 서쪽, 남쪽과 북쪽에 따라 lrud 배열의 [0]번 배열을 조작할지 [1]번 배열을 조작할 지 결정

 - 동쪽과 남쪽, 서쪽과 북쪽에 따라 배열 내 값들을 왼쪽으로 밀지, 오른쪽으로 밀지 결정

// turn : 주사위를 이동시키는 메서드
	static void turn(int dir) {

		// dir : 명령으로 주어진 이동 방향

		// tmp : 주사위 값 변경시 임시로 사용할 변수
		int tmp = 0;
		switch (dir) {

		// 동쪽
		case 1:

			// lrud[0]의 값들을 왼쪽으로 밀어 이동시킨다. (동쪽면에 있는 숫자를 바닥면으로, 바닥면에 있는 숫자를 서쪽면으로)
			tmp = lrud[0][0];
			for (int i = 0; i < 3; ++i) {
				lrud[0][i] = lrud[0][i + 1];
			}
			lrud[0][3] = tmp;
			break;

		// 서쪽
		case 2:

			// lrud[0]의 값들을 오른쪽으로 밀어 이동시킨다. (동쪽면에 있는 숫자를 윗면으로, 바닥면에 있는 숫자를 동쪽면으로)
			tmp = lrud[0][3];
			for (int i = 3; i > 0; --i) {
				lrud[0][i] = lrud[0][i - 1];
			}
			lrud[0][0] = tmp;
			break;

		// 북쪽
		case 3:

			// lrud[1]의 값들을 오른쪽으로 밀어 이동시킨다. (북쪽면에 있는 숫자를 바닥면으로, 바닥면에 있는 숫자를 남쪽면으로)
			tmp = lrud[1][3];
			for (int i = 3; i > 0; --i) {
				lrud[1][i] = lrud[1][i - 1];
			}
			lrud[1][0] = tmp;
			break;

		// 남쪽
		case 4:

			// lrud[1]의 값들을 왼쪽으로 밀어 이동시킨다. (북쪽면에 있는 숫자를 윗면으로, 바닥면에 있는 숫자를 북쪽면으로)
			tmp = lrud[1][0];
			for (int i = 0; i < 3; ++i) {
				lrud[1][i] = lrud[1][i + 1];
			}
			lrud[1][3] = tmp;
			break;
		}
	}

3. 특별히 고려해야 할 사항은?

 - 문제에서 주어지는 조건들을 유심히 보고 고려하면 크게 문제는 없을 것 같으나 문제를 읽어야 한다.

 - 주사위를 굴릴 때 주사위 내 값들을 어떤식으로 저장할 지에 대한 아이디어를 도출하는 것이 중요한 것 같다.


🐕‍🦺 소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
	static int N, M, r, c, K;
	static int[][] map;

	// {동, 서, 북, 남} 순서
	static int[] dc = new int[] { 1, -1, 0, 0, };
	static int[] dr = new int[] { 0, 0, -1, 1 };

	// 상 : 주사위의 윗면, 하 : 주사위의 바닥면
	// lrud[0] : {상, 서, 하, 동} - 동서 방향으로 이동시 사용
	// lrud[1] : {상, 북, 하, 남} - 남북 방향으로 이동시 사용
	static int[][] lrud = new int[][] { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };

	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		StringBuilder sb = new StringBuilder();

		// 변수 입력
		N = Integer.parseInt(st.nextToken());
		M = Integer.parseInt(st.nextToken());
		r = Integer.parseInt(st.nextToken());
		c = Integer.parseInt(st.nextToken());
		K = Integer.parseInt(st.nextToken());

		map = new int[N][M];

		// 지도 생성
		for (int i = 0; i < N; ++i) {
			st = new StringTokenizer(br.readLine());
			for (int j = 0; j < M; ++j) {
				map[i][j] = Integer.parseInt(st.nextToken());
			}
		}

		// 명령 수행
		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < K; ++i) {

			// input : 명령으로 주어진 이동 방향
			int input = Integer.parseInt(st.nextToken());

			// nr, nc : 명령에 따라 이동시 도달할 새 좌표
			int nr = r + dr[input - 1];
			int nc = c + dc[input - 1];

			// 좌표의 유효성 검사
			if (nr >= 0 && nc >= 0 && nr < N && nc < M) {

				// 유효한 좌표일 경우 주사위를 이동시키는 turn 메서드 수행
				turn(input);

				// 도착한 위치에서의 지도에 적혀져 있는 숫자가 0일 경우
				if (map[nr][nc] == 0) {

					// 주사위 바닥면에 있는 숫자를 지도에 옮겨 적는다.
					map[nr][nc] = lrud[(input ^ 8) / 11][2];
					// input^8 : input이 1일 때 부터 4일 때 까지 순서대로 9 10 11 12 의 값이 나온다.
					// (input^8)/11 : input이 1, 2일 경우 [0], input이 3, 4일 경우 [1]
				}

				// 도착한 위치에서의 지도에 적혀져 있는 숫자가 0이 아닐 경우
				else {

					// 지도에 적혀져 있는 숫자를 주사위 바닥면에 옮겨 적는다.
					lrud[(input ^ 8) / 11][2] = map[nr][nc];

					// 지도의 숫자를 0으로 지운다.
					map[nr][nc] = 0;
				}

				// 좌표 갱신
				r = nr;
				c = nc;

				// lrud 배열에서 상, 하 값을 기록한 부분을 동일하게끔 유지
				lrud[((input ^ 8) / 11) ^ 1][0] = lrud[(input ^ 8) / 11][0];
				lrud[((input ^ 8) / 11) ^ 1][2] = lrud[(input ^ 8) / 11][2];

				// 주사위 윗면 값을 StringBuilder에 저장
				sb.append(lrud[(input ^ 8) / 11][0]).append("\n");
			}
		}

		// 결과 출력
		System.out.println(sb);
	}

	// turn : 주사위를 이동시키는 메서드
	static void turn(int dir) {

		// dir : 명령으로 주어진 이동 방향

		// tmp : 주사위 값 변경시 임시로 사용할 변수
		int tmp = 0;
		switch (dir) {

		// 동쪽
		case 1:

			// lrud[0]의 값들을 왼쪽으로 밀어 이동시킨다. (동쪽면에 있는 숫자를 바닥면으로, 바닥면에 있는 숫자를 서쪽면으로)
			tmp = lrud[0][0];
			for (int i = 0; i < 3; ++i) {
				lrud[0][i] = lrud[0][i + 1];
			}
			lrud[0][3] = tmp;
			break;

		// 서쪽
		case 2:

			// lrud[0]의 값들을 오른쪽으로 밀어 이동시킨다. (동쪽면에 있는 숫자를 윗면으로, 바닥면에 있는 숫자를 동쪽면으로)
			tmp = lrud[0][3];
			for (int i = 3; i > 0; --i) {
				lrud[0][i] = lrud[0][i - 1];
			}
			lrud[0][0] = tmp;
			break;

		// 북쪽
		case 3:

			// lrud[1]의 값들을 오른쪽으로 밀어 이동시킨다. (북쪽면에 있는 숫자를 바닥면으로, 바닥면에 있는 숫자를 남쪽면으로)
			tmp = lrud[1][3];
			for (int i = 3; i > 0; --i) {
				lrud[1][i] = lrud[1][i - 1];
			}
			lrud[1][0] = tmp;
			break;

		// 남쪽
		case 4:

			// lrud[1]의 값들을 왼쪽으로 밀어 이동시킨다. (북쪽면에 있는 숫자를 윗면으로, 바닥면에 있는 숫자를 북쪽면으로)
			tmp = lrud[1][0];
			for (int i = 0; i < 3; ++i) {
				lrud[1][i] = lrud[1][i + 1];
			}
			lrud[1][3] = tmp;
			break;
		}
	}
}


🐖 Think

 - 시뮬레이션 문제에 많이 약해서 문제풀이를 해보고 있는데, 가장 중요한 것은 초기 설계인 것 같다.

 - 문제의 조건을 잘 살펴보고 어떤 메서드들을 작성해야 하는지, 이를 위해 어떤 자료구조를 이용해야 하는지, 메서드를 실행시켰을 때 예외상황은 없는지 등을 손으로 풀어보며 설계하니 보다 명확하게 문제풀이가 가능한 것 같다. 이를 습관화 해야겠다.

반응형