주사위 굴리기
저번에 포스팅한 문제와 마찬가지로 현실 세계의 복잡한 문제를 컴퓨터가 해석할 수 있게끔
단순화를 하는 게 중요한 문제입니다.
이런 문제는 무작정 코딩부터 하지 말고
하나하나 차근차근 조건들과 톱니바퀴의 동작이 어떻게 돌아갈건지 정리를 한 다음
코드를 짜는 게 문제를 푸는 제일 빠른 길입니다.
계획 1 - 4개의 톱니바퀴들을 2차원 배열에 각각 집어넣습니다.
별 거 아닌 단계같지만 현실 세계의 문제를 어떻게 컴퓨터의 데이터화를 시킬 것인가
결정하는 부분이기 때문에 이 단계에서 절반은 풀었다 생각해도 과언은 아닙니다.
// 4개의 톱니바퀴들의 정보를 담을 배열을 선언
int gears[][] = new int[4][8]
for (int i = 0; i < 4; i++) {
String s = br.readLine();
for (int j = 0; j < 8; j++)
gears[i][j] = s.charAt(j) - '0';
}
계획 2 - 4개의 톱니바퀴들이 서로 맞닿은 부분을 테이블로 정의합니다.
서로 맞닿은 부분들이 같은 극인지 다른 극인지 확인하는 로직은 계속 반복되기 때문에
테이블로 정의해놓으면 중복된 코드 없이 깔끔하게 로직을 작성할 수 있습니다.
int[][][] meet = {
// 0번째 톱니바퀴의 2번 톱니는 1번째 톱니바퀴의 6번 톱니와 맞닿는다.
{{2, 1, 6}},
// 1번째 톱니바퀴의 6번 톱니는 0번째 톱니바퀴의 2번 톱니와 맞닿는다.
// 1번째 톱니바퀴의 2번 톱니는 2번째 톱니바퀴의 6번 톱니와 맞닿는다.
{{6, 0, 2}, {2, 2, 6}},
// 이하 생략...
{{6, 1, 2}, {2, 3, 6}},
{{6, 2, 2}}
};
계획 3 - 임의의 톱니바퀴를 돌릴 때 연쇄작용으로 돌려질 톱니바퀴들과 방향을 기록합니다.
현실의 톱니바퀴처럼 돌리면서 연쇄작용을 그대로 구현하는 건 매우 어렵습니다.
돌리기 전에 어떻게 연쇄작용이 일으켜져서 톱니바퀴들이 어떻게 돌아가는지 정보를 저장해놓은 다음에
돌려도 늦지 않습니다.
저는 재귀함수로 기록해보겠습니다.
이 때 계획 2번에서 정의한 테이블을 이용합니다.
// 재귀적으로 num번째 톱니바퀴를 dir방향으로 돌아간다 기록하는 함수
static void check(int num, int dir) {
// 기록
rollDir[num] = dir;
// num번째 톱니바퀴와 맞닿은 톱니바퀴들에 대해 연쇄작용을 검사
for (int i = 0; i < meet[num].length; i++) {
int[] v = meet[num][i];
// 맞닿은 톱니바퀴의 돌릴 방향이 정해지지 않았고,
// 서로 극이 다르다면
if (rollDir[v[1]] == 0 && gears[num][v[0]] != gears[v[1]][v[2]])
check(v[1], -dir); // 재귀호출로 연쇄작용을 일으켜 돌릴 방향 기록
}
}
계획 4 - 톱니바퀴를 돌립니다.
톱니바퀴를 어떻게 돌릴지 기록했으면 남은 일은 실제로 돌려보는 일이겠죠.
톱니바퀴는 1차원 배열로 데이터화 되었으니
돌리는 방향에 따라서 배열의 데이터를 한 칸씩 앞으로 밀거나 뒤로 밀면 됩니다.
// 톱니바퀴를 dir 방향으로 돌리는 함수
static void roll(int[] gear, int dir) {
boolean isClock = dir == 1;
int tmp = isClock ? gear[7] : gear[0];
int s = isClock ? 6 : 1;
for (int i = 0; i < 7; i++) {
gear[s + dir] = gear[s];
s -= dir;
}
gear[isClock ? 0 : 7] = tmp;
}
계획 5 - 톱니바퀴의 상태를 계산하고 출력
문제에서 요구하는 대로 각 톱니바퀴의 맨 위 톱니의 극이 S극일 때만 스코어를 더해줘서
답을 출력합니다.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
static int[][][] meet = {
{{2, 1, 6}},
{{6, 0, 2}, {2, 2, 6}},
{{6, 1, 2}, {2, 3, 6}},
{{6, 2, 2}}
};
static int gears[][] = new int[4][8], rollDir[] = new int[4], score[] = {1, 2, 4, 8}, K;
// 재귀적으로 num번째 톱니바퀴를 dir방향으로 돌아간다 기록하는 함수
static void check(int num, int dir) {
// 기록
rollDir[num] = dir;
// num번째 톱니바퀴와 맞닿은 톱니바퀴들에 대해 연쇄작용을 검사
for (int i = 0; i < meet[num].length; i++) {
int[] v = meet[num][i];
// 맞닿은 톱니바퀴의 돌릴 방향이 정해지지 않았고,
// 서로 극이 다르다면
if (rollDir[v[1]] == 0 && gears[num][v[0]] != gears[v[1]][v[2]])
check(v[1], -dir); // 재귀호출로 연쇄작용을 일으켜 돌릴 방향 기록
}
}
// 톱니바퀴를 dir 방향으로 돌리는 함수
static void roll(int[] gear, int dir) {
boolean isClock = dir == 1;
int tmp = isClock ? gear[7] : gear[0];
int s = isClock ? 6 : 1;
for (int i = 0; i < 7; i++) {
gear[s + dir] = gear[s];
s -= dir;
}
gear[isClock ? 0 : 7] = tmp;
}
public static void main(String[] args) throws Exception {
// 1. 4개의 톱니바퀴들을 2차원 배열에 각각 집어넣습니다.
for (int i = 0; i < 4; i++) {
String s = br.readLine();
for (int j = 0; j < 8; j++)
gears[i][j] = s.charAt(j) - '0';
}
K = Integer.parseInt(br.readLine());
for (int i = 0; i < K; i++) {
String[] s = br.readLine().split(" ");
int num = Integer.parseInt(s[0]) - 1, dir = Integer.parseInt(s[1]);
// 2. 임의의 톱니바퀴를 돌릴 때 연쇄작용으로 돌려질 톱니바퀴들과 방향을 기록합니다.
check(num, dir);
// 3. 톱니바퀴를 돌립니다.
for (int j = 0; j < 4; j++)
if (rollDir[j] != 0)
roll(gears[j], rollDir[j]);
// 기록 초기화
for (int j = 0; j < 4; j++)
rollDir[j] = 0;
}
// 4. 톱니바퀴의 상태를 계산합니다.
int ans = 0;
for (int i = 0; i < 4; i++)
if (gears[i][0] == 1)
ans += score[i];
bw.write(ans + "");
bw.close();
}
}