[JAVA] BOJ_14499. 주사위 굴리기
🦍 문제
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
- 시뮬레이션 문제에 많이 약해서 문제풀이를 해보고 있는데, 가장 중요한 것은 초기 설계인 것 같다.
- 문제의 조건을 잘 살펴보고 어떤 메서드들을 작성해야 하는지, 이를 위해 어떤 자료구조를 이용해야 하는지, 메서드를 실행시켰을 때 예외상황은 없는지 등을 손으로 풀어보며 설계하니 보다 명확하게 문제풀이가 가능한 것 같다. 이를 습관화 해야겠다.