백준 - 톱니바퀴 (14891)

아놀드·2021년 8월 11일
0

백준

목록 보기
13/73

1. 문제

문제 링크





2. 풀이

2-1. 조건

  1. 임의의 톱니바퀴가 회전할 때 맞닿은 다른 톱니바퀴의 톱니와 극이 다르면 반대 방향으로 회전하고 같으면 회전하지 않는다.

2-2. 풀이

주사위 굴리기
저번에 포스팅한 문제와 마찬가지로 현실 세계의 복잡한 문제를 컴퓨터가 해석할 수 있게끔
단순화를 하는 게 중요한 문제입니다.

이런 문제는 무작정 코딩부터 하지 말고
하나하나 차근차근 조건들과 톱니바퀴의 동작이 어떻게 돌아갈건지 정리를 한 다음
코드를 짜는 게 문제를 푸는 제일 빠른 길입니다.

계획 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극일 때만 스코어를 더해줘서
답을 출력합니다.

3. 전체 코드

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();
	}
}
profile
함수형 프로그래밍, 자바스크립트에 관심이 많습니다.

0개의 댓글