[백준/Java] 14891- 톱니바퀴

승래·2026년 1월 27일

14891- 톱니바퀴

문제 바로가기

1. 문제 요구사항

  • 입력: 4개의 톱니바퀴 상태(N극/S극, 8개 톱니)와 KK번의 회전 명령(회전시킬 바퀴 번호, 방향).
  • 규칙:
    • 톱니바퀴를 회전시킬 때, 맞닿은 극(3시 방향 vs 9시 방향)이 서로 다르면 옆에 있는 톱니바퀴는 반대 방향으로 회전한다.
    • 맞닿은 극이 같으면 옆에 있는 톱니바퀴는 회전하지 않는다.
    • 옆 톱니바퀴가 회전하지 않으면, 그 옆의 톱니바퀴도 당연히 회전하지 않는다.
  • 출력: 모든 회전이 끝난 후, 각 톱니바퀴의 12시 방향 극(N/S)에 따른 점수의 합을 출력한다.

2. 접근 방식 (Algorithm)

이 문제는 "동시성" 처리가 가장 중요하다. 톱니바퀴를 하나씩 바로바로 돌려버리면, 다음 바퀴가 회전 여부를 판단할 때 이미 돌아가버린 극을 참조하게 되어 오답이 발생한다.

따라서 다음과 같은 3단계 로직으로 설계했다.

1) 회전 방향 결정 (Pre-check)

  • check 또는 direct 함수를 만들어 4개의 톱니바퀴가 각각 어느 방향으로 돌지 저장할 배열(int[] dir)을 만든다.
  • 왼쪽 전파: 선택된 바퀴를 기준으로 왼쪽(i-1)으로 가면서, 내 9시(index 6)왼쪽의 3시(index 2)를 비교한다.
  • 오른쪽 전파: 선택된 바퀴를 기준으로 오른쪽(i+1)으로 가면서, 내 3시(index 2)오른쪽의 9시(index 6)를 비교한다.
  • 핵심 로직: 극이 다르면 dir[i] = -dir[기준] (반대 방향), 극이 같으면 break (전파 중단).

2) 회전 수행 (Rotate)

  • 방향 결정이 끝나면, dir 배열을 확인하여 실제로 배열을 회전시킨다.
  • 시계 방향(1): temp를 이용해 뒤에서 앞으로 이동.
  • 반시계 방향(-1): temp를 이용해 앞에서 뒤로 이동.

3) 점수 계산

  • 최종 상태의 12시 방향(index 0)을 확인한다.
  • 1번(1점), 2번(2점), 3번(4점), 4번(8점)은 20,21,22,232^0, 2^1, 2^2, 2^3과 같으므로 비트 연산(1 << i)을 활용해 합산한다.

3. 소스 코드 (Java)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[][] arr;

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

        arr = new int[4][8];
        for(int i=0; i<4; i++) {
            st = new StringTokenizer(br.readLine());
            String line = st.nextToken();
            for(int j=0; j<8; j++) {
                arr[i][j] = line.charAt(j) - '0';
            }
        }

        st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        for(int i=0; i<n; i++) {
            st = new StringTokenizer(br.readLine());
            int num = Integer.parseInt(st.nextToken());
            int di = Integer.parseInt(st.nextToken());

            int[] dir = new int[4];
            direct(num-1, di, dir);
            rotate(dir);
        }

        int answer = 0;
        for(int i=0; i<4; i++) {
            if(arr[i][0] == 1) {
                answer += (1 << i);
            }
        }
        System.out.println(answer);
    }

    static void rotate(int[] dir) {
        for(int i=0; i<4; i++) {
            if(dir[i] == 1) {
                int temp = arr[i][7];
                for(int j=7; j>0; j--) {
                    arr[i][j] = arr[i][j-1];
                }
                arr[i][0] = temp;
            }
            else if(dir[i] == -1) {
                int temp = arr[i][0];
                for(int j=0; j<7; j++) {
                    arr[i][j] = arr[i][j+1];
                }
                arr[i][7] = temp;
            }
        }
    }

    static void direct(int num, int di, int[] dir) {
        dir[num] = di;

        for(int i = num-1; i >= 0; i--) {
            if(arr[i+1][6] != arr[i][2]) {
                dir[i] = dir[i+1] * -1;
            }
            else {
                break;
            }
        }

        for(int i = num+1; i < 4; i++) {
            if(arr[i-1][2] != arr[i][6]) {
                dir[i] = dir[i-1] * -1;
            }
            else {
                break;
            }
        }
    }
}

4. 느낀점 & 배운점

  1. 방향 전파 로직의 중요성

처음에는 단순히 기준 방향(di)에 -1을 곱해서 전파시켰는데, 이렇게 하면 1 -> -1 -> 1로 이어져야 할 흐름이 1 -> -1 -> -1로 끊기는 오류가 발생했다.

기준이 되는 di가 아니라, "내 바로 옆 톱니바퀴(dir[i+1] or dir[i-1])의 반대 방향"으로 설정해야 올바르게 연쇄 작용이 일어난다는 점을 깨달았다.

  1. 구현 순서 (판단 후 실행)

"일단 돌리고 나서 옆을 확인하자"라고 생각하면 꼬이기 쉬운 문제다. "누가 돌지 다 정해놓고(dir 배열), 마지막에 한꺼번에 돌린다"는 시뮬레이션의 정석 패턴을 익힐 수 있었다.

  1. 비트 연산 활용

점수 계산 시 Math.pow 대신 1 << i를 사용하니 코드가 훨씬 간결해졌다.

profile
힘들어도 조금만 더!

0개의 댓글