백준 #15662 - 톱니바퀴 (2) (Java)

베이시스·2022년 6월 11일
0

알고리즘 - 백준

목록 보기
3/6
post-custom-banner

⚠️ 유의사항
필자는 가독성과 구조화에 중점을 두고 문제를 풀이합니다.
필요하다면 클래스를 자주 사용하므로 절차지향 언어로 문제해결을 한다면 적용이 어려울 수 있습니다.

🔗 링크

https://www.acmicpc.net/problem/15662

✏️ 문제

주어진 조건에 따라 톱니바퀴를 회전시킨 후, 12시 방향이 S극인 톱니바퀴의 개수를 구하는 문제입니다.

🧠 접근

구현 난도가 마냥 낮지는 않지만 지문에서 설명하는 대로 구현하면 해결할 수 있는 문제입니다.

지문이 조금 아리까리하게 작성되어 있어서 이게 맞나... 하면서 여러번 지문을 돌려보고 부족한 필자의 문해력을 탓하게 된 문제이기도 합니다.

주의해야 할 사항은 아래 세 가지로 정리할 수 있습니다.

  • 처음에 돌아가는 톱니바퀴는 맞물림 여부와 관계없이 반드시 돌아갑니다.
  • 맞물림이 되지 않은 톱니바퀴 이후의 모든 톱니바퀴는 돌아가지 않습니다.
  • 모든 맞물림 여부는 돌아가기 전에 확인해야 합니다.

톱니바퀴 구현

처음에는 덱을 활용하여 구현할까 생각했으나 맞물림 위치와 구해야 하는 톱니의 위치가 다르기에 효율적인 구현이라고 볼 수 없습니다.

특정 톱니의 값을 가져오는 데 굳이 추가 순회를 해서 확인하게 하는 건 품이 많이 들고, 그렇다고 톱니의 개수가 늘어나거나 줄어드는 것도 아니니까요.

톱니의 개수는 고정되어 있으니 톱니의 기본 상태를 배열에 저장해 두고 인덱스를 시작점으로 둔 뒤 회전에 따라 인덱스를 바꾸면 되겠다고 판단하였습니다.

class Gear {
    private int[] gear; // 톱니바퀴의 초기 상태
    private int currentPos; // 톱니바퀴의 초기 인덱스

    public Gear(int[] array) {
        gear = array;
        currentPos = 0;
    }

	// 왼쪽으로 돌아가면 인덱스 값은 증가함
    public void rollLeft() {
        currentPos = (currentPos + 1) % 8;
    }
	
    // 오른쪽으로 가면 인덱스 값은 감소함
    public void rollRight() {
        if (currentPos == 0) currentPos = 7;
        else currentPos -= 1;
    }

	// 인덱스 기준으로 각각 12시, 3시, 9시 값을 가져옴
    // 3시와 9시는 톱니바퀴가 맞물리는 지점
    public int get12() {
        return gear[currentPos];
    }

    public int get3() {
        return gear[(currentPos + 2) % 8];
    }

    public int get9() {
        return gear[(currentPos + 6) % 8];
    }
}

규칙에 따라 톱니바퀴를 돌리기

사실 필요한 모든 사항이 클래스로 구현되었기에 상술한 주의점을 생각하면서 규칙에 따라 구현하기만 하면 됩니다.

  1. 먼저 톱니바퀴가 회전하는 방법을 저장해 둘 배열을 선언합니다.
  2. 시작점은 반드시 돌아가도록 배열에 기록합니다.
  3. 왼쪽과 오른쪽으로 각각 순회하면서 톱니바퀴의 회전 여부를 기록합니다.
  4. 기록해 둔 배열을 이용해 톱니바퀴를 돌립니다.
int rollCount = Integer.parseInt(reader.readLine());

for (int i = 0; i < rollCount; i++) {
	String[] input = reader.readLine().split(" ");
    int pos = Integer.parseInt(input[0]) - 1;
	int direction = Integer.parseInt(input[1]); // 1 : 시계 방향, -1 : 반시계 방향
	int[] roll = new int[gearCount];

    // 현 위치를 회전시킴
    roll[pos] = direction;

    // 왼쪽 순회
    for (int j = pos - 1; j > -1; j--) {
    	Gear current = gears[j];
        Gear prev = gears[j + 1];
            
        if (prev.get9() == current.get3()) 
        	break;
        else {
        	roll[j] = -1 * roll[j + 1]; // 반대 방향으로 회전
		}
	}
	// 오른쪽 순회
    for (int j = pos + 1; j < gearCount; j++) {
    	Gear current = gears[j];
        Gear prev = gears[j - 1];

        if (prev.get3() == current.get9()) {
        	break;
        } else {
        	roll[j] = -1 * roll[j - 1];
        }
    }

    // 실제 회전
    for (int j = 0; j < gearCount; j++) {
    	if (roll[j] == 1) gears[j].rollRight();
        	if (roll[j] == -1) gears[j].rollLeft();
    }
}

이제 12시 방향이 S극인지 확인하고 정답을 출력해주면 끝납니다.

int count = 0;
for (int i = 0; i < gearCount; i++)
	if (gears[i].get12() == 1)
		count += 1;

System.out.println(count);

📄 전체 소스 코드

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int gearCount = Integer.parseInt(reader.readLine());
        Gear[] gears = new Gear[gearCount];

        for (int i = 0; i < gearCount; i++) 
            gears[i] = new Gear(Arrays.stream(reader.readLine().split("")).mapToInt(Integer::parseInt).toArray());

        int rollCount = Integer.parseInt(reader.readLine());

        for (int i = 0; i < rollCount; i++) {
            String[] input = reader.readLine().split(" ");
            int pos = Integer.parseInt(input[0]) - 1;
            int direction = Integer.parseInt(input[1]); // 1 : 시계 방향, -1 : 반시계 방향
            int[] roll = new int[gearCount];

            // 현 위치를 회전시킴
            roll[pos] = direction;

            // 왼쪽 순회
            for (int j = pos - 1; j > -1; j--) {
                Gear current = gears[j];
                Gear prev = gears[j + 1];
            
                if (prev.get9() == current.get3()) 
                    break;
                else {
                    roll[j] = -1 * roll[j + 1]; // 반대 방향으로 회전
                }
            }

            // 오른쪽 순회
            for (int j = pos + 1; j < gearCount; j++) {
                Gear current = gears[j];
                Gear prev = gears[j - 1];

                if (prev.get3() == current.get9()) {
                    break;
                } else {
                    roll[j] = -1 * roll[j - 1];
                }
            }

            // 실제 회전
            for (int j = 0; j < gearCount; j++) {
                if (roll[j] == 1) gears[j].rollRight();
                if (roll[j] == -1) gears[j].rollLeft();
            }
        }

        int count = 0;
        for (int i = 0; i < gearCount; i++)
            if (gears[i].get12() == 1)
                count += 1;

        System.out.println(count);        
    }  
}

class Gear {
    private int[] gear;
    private int currentPos;

    public Gear(int[] array) {
        gear = array;
        currentPos = 0;
    }

    public void rollLeft() {
        currentPos = (currentPos + 1) % 8;
    }

    public void rollRight() {
        if (currentPos == 0) currentPos = 7;
        else currentPos -= 1;
    }

    public int get12() {
        return gear[currentPos];
    }

    public int get3() {
        return gear[(currentPos + 2) % 8];
    }

    public int get9() {
        return gear[(currentPos + 6) % 8];
    }
}

❗️이 문제와 유사한 문제

사실 거의 똑같은 문제이고, 이 문제를 푸셨다면 14891번은 몇 줄만 고치면 맞힐 수 있습니다.
그 반대는 조금은 생각해서 풀어야겠지만요.

profile
사진찍는 주니어 프론트엔드 개발자
post-custom-banner

0개의 댓글