1. 문제 설명
2. 문제 접근
3. 문제 풀이
4. 전체 코드
5. 결과 및 평가
링크
1. 문제 설명
크기가 N×M인 지도가 존재한다. 지도의 오른쪽은 동쪽, 위쪽은 북쪽이다. 이 지도의 위에 주사위가 하나 놓여져 있으며, 주사위의 전개도는 아래와 같다. 지도의 좌표는 (r, c)로 나타내며, r는 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로부터 떨어진 칸의 개수이다.
2
4 1 3
5
6
주사위는 지도 위에 윗 면이 1이고, 동쪽을 바라보는 방향이 3인 상태로 놓여져 있으며, 놓여져 있는 곳의 좌표는 (x, y) 이다. 가장 처음에 주사위에는 모든 면에 0이 적혀져 있다.
지도의 각 칸에는 정수가 하나씩 쓰여져 있다. 주사위를 굴렸을 때, 이동한 칸에 쓰여 있는 수가 0이면, 주사위의 바닥면에 쓰여 있는 수가 칸에 복사된다. 0이 아닌 경우에는 칸에 쓰여 있는 수가 주사위의 바닥면으로 복사되며, 칸에 쓰여 있는 수는 0이 된다.
주사위를 놓은 곳의 좌표와 이동시키는 명령이 주어졌을 때, 주사위가 이동했을 때 마다 상단에 쓰여 있는 값을 구하는 프로그램을 작성하시오.
주사위는 지도의 바깥으로 이동시킬 수 없다. 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다.
입력
첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x, y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다.
둘째 줄부터 N개의 줄에 지도에 쓰여 있는 수가 북쪽부터 남쪽으로, 각 줄은 서쪽부터 동쪽 순서대로 주어진다. 주사위를 놓은 칸에 쓰여 있는 수는 항상 0이다. 지도의 각 칸에 쓰여 있는 수는 10 미만의 자연수 또는 0이다.
마지막 줄에는 이동하는 명령이 순서대로 주어진다. 동쪽은 1, 서쪽은 2, 북쪽은 3, 남쪽은 4로 주어진다.
출력
이동할 때마다 주사위의 윗 면에 쓰여 있는 수를 출력한다. 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다.
2. 문제 접근
구현/시뮬레이션 문제다. 정해진 알고리즘이 있지 않고 상황을 그대로 옮겨오기만 하면 된다. 주사위를 어떻게 굴려야할지 고민하는 시간이 길었다.

주사위 전개도를 접으면 저렇게 생겼다. 그리고 주사위를 지도에 맞춰서 예쁘게 굴리니까,

위아래(북쪽/남쪽)로 굴릴때, 위치가 변했다고 할 수 있는 숫자는 저렇게 네 개다.

비슷한 맥락으로 좌우(동쪽/서쪽)로 굴릴때는 저렇게 네 개의 숫자만 바뀐다.
그래서 처음에는 위아래와 좌우 두 개의 구조를 만들어서 윗면 인덱스만 가지고 있게 하려고 했는데, 주사위는 원래 하나라서 두 개를 따로 관리하는 것도 문제가 있고 없는 면이 윗면일수도 있고 숫자도 계속 바뀌는데 다 고려해줘야하고.... 아무튼 여러 이유로 불가능했다.
그래서 지도와의 상호작용 + 굴리는 것 자체는 복잡하게 바뀌지 않음! 이라는 이유로 주사위가 돌아간다기보단 주사위 위치는 정해져있고, 주사위를 굴릴 때마다 숫자가 바뀌는 것처럼 구현하기로 했다.
3. 문제 풀이
우선 위치별로 자리를 지정해주고(인덱스 부여), 위의 주사위를 예시로 보겠다.

인덱스야 뭐,... 사람 마음대로 부여하면 된다. 나는 가로로 굴릴 때 바뀌는 곳이랑 세로로 굴릴 때 바뀌는 곳이 대칭이면 좋겠어서(?) 저렇게 붙여줬다.
아까 파란색 노란색 칠했던 가로세로 사진 기준으로 보자면 각 인덱스 따라서 바뀌는 곳이 아래 사진처럼 된다. (색으로 표시함. 검은색은 현재 주사위의 숫자 기준으로 볼 때)

즉, 북쪽으로 주사위를 굴리면 숫자가
인덱스 기준 0 <- 1 <- 3 <- 4 <- 0 이렇게 회전한다.
굳이 회전한 뒤의 주사위 그림도 보자면

대충 이렇게 생겼을 것이다.
동/서/남/북 주사위 로테이션만 잘 구현해주면 문제 풀이는 거의 끝난다! 코드로 보자면 아래와 같다.
static void dice1() { // 동쪽
int temp = dice[5];
dice[5] = dice[3];
dice[3] = dice[2];
dice[2] = dice[0];
dice[0] = temp;
}
static void dice2() { // 서쪽
int temp = dice[0];
dice[0] = dice[2];
dice[2] = dice[3];
dice[3] = dice[5];
dice[5] = temp;
}
static void dice3() { // 북쪽
int temp = dice[0];
dice[0] = dice[1];
dice[1] = dice[3];
dice[3] = dice[4];
dice[4] = temp;
}
static void dice4() { // 남쪽
int temp = dice[4];
dice[4] = dice[3];
dice[3] = dice[1];
dice[1] = dice[0];
dice[0] = temp;
}
java
북쪽과 마찬가지로 도형을 보고 생각해서 숫자를 돌려준다.
맵과의 상호작용과 윗면에 적힌 숫자 출력은 아래처럼 구현했다.
static void map() {
if(map[x][y] == 0) { // 이동한 칸의 수가 0이면
map[x][y] = dice[3]; // 주사위 바닥에 있는 수가 그 칸에 복사 됨
}
else { // 아니면 칸에 쓰인 수가 주사위 바닥으로
dice[3] = map[x][y];
map[x][y] = 0;
}
sb.append(dice[0] + "\n");
}
java
주사위는 가만히 있고 안의 숫자만 돌아가도록 만들었기 때문에, 몇 번을 굴리든 윗면은 dice[0]에 있다. 마찬가지로 바닥면은 dice[3]에 있게 된다. 코드가 매우 간단하다.
이제 전체 구조는, 주사위를 굴리라는 명령이 들어올 때마다 아래의 순서를 따라주면 된다.
- 가능한 명령인지 판단하고
- 가능하다면 주사위를 굴린다.불가능하면 다음으로 넘어간다.
- 굴린 후에는 지도와의 상호작용을 처리한다.
- 윗면의 숫자를 출력한다.
4. 전체 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class BOJ14499 {
static int N, M, K;
static int x,y;
static int[][] map;
static int[] dice = new int[6];
static StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
x = Integer.parseInt(st.nextToken());
y = 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++) {
int roll = Integer.parseInt(st.nextToken());
if(roll == 1) {
if(check(0, 1)) {
dice1();
map();
}
} else if(roll == 2) {
if(check(0, -1)) {
dice2();
map();
}
} else if(roll == 3) {
if(check(-1, 0)) {
dice3();
map();
}
} else { // roll == 4
if(check(1, 0)) {
dice4();
map();
}
}
}
System.out.println(sb.toString());
}
static boolean check(int _x, int _y) { // 가능한 명령인지 체크하고 가능하면 위치 이동
if( x+_x >= N || x+_x <0 || y+_y >= M || y+_y < 0) return false;
x = x+_x;
y = y+_y;
return true;
}
static void map() {
if(map[x][y] == 0) { // 이동한 칸의 수가 0이면
map[x][y] = dice[3]; // 주사위 바닥에 있는 수가 그 칸에 복사 됨
}
else { // 아니면 칸에 쓰인 수가 주사위 바닥으로
dice[3] = map[x][y];
map[x][y] = 0;
}
sb.append(dice[0] + "\n");
}
static void dice1() { // 동쪽
int temp = dice[5];
dice[5] = dice[3];
dice[3] = dice[2];
dice[2] = dice[0];
dice[0] = temp;
}
static void dice2() { // 서쪽
int temp = dice[0];
dice[0] = dice[2];
dice[2] = dice[3];
dice[3] = dice[5];
dice[5] = temp;
}
static void dice3() { // 북쪽
int temp = dice[0];
dice[0] = dice[1];
dice[1] = dice[3];
dice[3] = dice[4];
dice[4] = temp;
}
static void dice4() { // 남쪽
int temp = dice[4];
dice[4] = dice[3];
dice[3] = dice[1];
dice[1] = dice[0];
dice[0] = temp;
}
}
java
5. 결과 및 평가

수정 떠있는 부분이 내 제출임. 구글링 없이 머리로만 푼 것 치고는 적당히 잘 푼 것 같아서 만족했다. 나보다 빠른 코드들을 살펴봤는데, 크게 다른 점은 없고 약간의 테크닉이랄까.... ㅎㅎ 구글링 없이 푼 기념으로 빠르게 정리해서 올린다.
'문제풀이' 카테고리의 다른 글
[백준 1107] 리모컨 - JAVA (0) | 2023.01.12 |
---|---|
[백준 10815] 숫자 카드 - JAVA (0) | 2022.01.07 |
[백준 1946] 신입 사원 - JAVA (0) | 2022.01.06 |
[백준 1697] 숨바꼭질 - JAVA (0) | 2022.01.05 |
Comment