BOJ 14891. 톱니바퀴 (Python)

Wooseok Jung·2023년 9월 19일
0

CodingTestStudy

목록 보기
5/5
post-thumbnail
post-custom-banner

4개의 맞물린 톱니바퀴가 있다. 각 톱니에는 자성(N극, S극)이 있고 지정된 톱니가 지정된 명령대로 회전한다. 그 결과를 구하라

조건

문제는 간단하게 생각할 수 있는 문제였다.

확실히 과거 삼성 기출이 지금보다 훨씬 쉬운거 같다..

  1. 톱니바퀴는 1,2,3,4 순으로 놓여져 있다.
  2. 옆의 톱니바퀴가 회전하면 그 다른 톱니바퀴는 다른 방향으로 회전한다.
  3. 각 톱니는 극성이 있는데 (N극 S극) 마주하는 극이 같으면 회전하고, 그렇지 않다면 정지한다.
  4. 명령에 따라 회전을 했을때, 최종 톱니바퀴의 12시 방향의 극성으로 점수를 계산하여 반환하라

Solution

필요했던 함수는 크게, 톱니를 회전시키는 함수, 회전 가능한지 확인하는 함수, 실제로 한 톱니가 돌았을 때 결과. 세가지가 필요했다.

rotate() -> List

우선 톱니가 회전한 결과를 반환하는 함수 rotate()를 작성했다.

def rotate(g : list, direction : int) -> list:
  """
  g :  gear list 
  direction : rotate direction, 1 : 시계방향, -1 반시계 방향
  return : rotated g
  """
  new_g = []
  if direction == 1:
    new_g = [g[-1]] + g[0:-1]
  elif direction == -1:
    new_g = g[1:] + [g[0]]
  return new_g

여기서 했던 실수중의 하나가,
톱니들이 0번 톱니 (12시 방향)부터 시계방향으로 리스트에 극성이 담긴 형태인데, 슬라이싱을 이용했더니, g[0] 혹은 g[-1]이 값이어서 브라켓을 씌워서 리스트화 한다음 남은 부분과 더하기 했다. (이 방식이 메모리 측면에서는 좋지 못하다는 정보를 봤다)

movable() -> Boolean

다음은 마주보는 톱니를 이용해서, 기준이 되는 톱니 basecompare를 이용해서 기준 톱니 기준 왼쪽인지 오른쪽인지 여부에 따라서 Boolean값을 반환하는 함수 movable을 작성했다.

def moveable(base : list, compare : list, direction : int) -> bool:
  """
  base : 기준이 되는 톱니
  compare : 움직일 톱니
  direction : base 기준 compare 위치, 
              1 : 왼쪽, 
              -1 : 오른쪽
  """
  # 왼쪽과 비교
  if direction == 1:
    return base[6] != compare[2]
  # 오른쪽과 비교
  elif direction == -1:
    return base[2] != compare[6]
  return "error"

처음에는 비트 연산 ^를 사용했는데, 그냥 달라야지만 움직이는 것으로 변경했다. (또 오른쪽 왼쪽 설정한걸 헷갈려서...오류가 있었다)

iteration() -> list

마지막으로 하나의 톱니가 돌았을때, 양 옆을 보며 톱니를 회전시키는 함수를 작성했다. 우선 오른쪽, 왼쪽을 나눠서 본다. 이때 코드가 중복되어 수정을 해보는 방안을 고민해 봤는데, 굳이 싶어서 그냥 두번 적었다. (사실 이런건 기술부채인가요...)

def iteration(gear : list, command : tuple) -> list:
  """
  gear : gear list
  command : (gear number, direction) direction, 1 : 시계방향, -1 반시계 방향
  """
  g, base_dir = command[0] -1, command[1]
  rotation = [(0,0)] * 4
  rotation[g] = (1, base_dir)

  # g 기준 왼쪽 오른쪽 분리 (인덱스로)
  left = [0,1,2,3][:g][::-1] # 역순으로 슬라이싱해서 루프 돌리기 편하게 함
  right = [0,1,2,3][g+1:]

  base = gear[g]
  dir = base_dir
  # base 기준 왼쪽
  for idx in left:
    if moveable(base, gear[idx], 1):
      base = gear[idx]
      dir = dir * -1
      rotation[idx] = (1, dir)
    else:
      break

  # base기준 오른쪽
  base = gear[g]
  dir = base_dir
  for idx in right:
    if moveable(base, gear[idx], -1):
      base = gear[idx]
      dir = dir * -1
      rotation[idx] = (1, dir)
    else:
      break

  new_gear = []
  for idx, g in enumerate(gear):
    if rotation[idx][0]:
      new_gear.append(rotate(g, rotation[idx][1]))
    else:
      new_gear.append(g)

  return new_gear

여기서 코드가 중복되면서 발생했던 문제점은,
기존 톱니가 도는 방향을 dir이라는 변수에 저장했다 (변수명도 적절한 것은 아닌거같다). 이때 왼쪽 기준으로 회전방향에 -1 을 곱해가면서 변경했는데, dir을 다시 기존 방향으로 갱신했어야 하는데 누락되어서 저 멀리 있는 톱니가 돌아가는 불상사가 벌어졌다.
Movable 하지 않으면 바로 루프를 탈출한다.

Sol() -> int

최종적으로 위의 조건처럼 12시의 톱니를 이용하여 최종 점수를 환산한다.

def sol(gear, K, move):
  for i in range(K):
    gear = iteration(gear, move[ i])

  return gear[0][0] + 2 * gear[1][0] + 4 * gear[2][0] + 8 * gear[3][0]

전체코드

# 톱니바퀴 https://www.acmicpc.net/problem/14891 

import sys

def InputData():
  readl = sys.stdin.readline
  gear = [[int(x) for x in readl().strip()] for _ in range(4)]
  K = int(readl())
  move = [tuple(map(int, readl().split())) for _ in range(K)]

  return gear, K, move


def rotate(g : list, direction : int) -> list:
  """
  g :  gear list 
  direction : rotate direction, 1 : 시계방향, -1 반시계 방향
  return : rotated g
  """
  new_g = []
  if direction == 1:
    new_g = [g[-1]] + g[0:-1]
  elif direction == -1:
    new_g = g[1:] + [g[0]]
  return new_g

def moveable(base : list, compare : list, direction : int) -> bool:
  """
  base : 기준이 되는 톱니
  compare : 움직일 톱니
  direction : base 기준 compare 위치, 
              1 : 왼쪽, 
              -1 : 오른쪽
  """
  # 왼쪽과 비교
  if direction == 1:
    return base[6] != compare[2]
  # 오른쪽과 비교
  elif direction == -1:
    return base[2] != compare[6]
  return "error"

def iteration(gear : list, command : tuple) -> list:
  """
  gear : gear list
  command : (gear number, direction) direction, 1 : 시계방향, -1 반시계 방향
  """
  g, base_dir = command[0] -1, command[1]
  rotation = [(0,0)] * 4
  rotation[g] = (1, base_dir)

  # g 기준 왼쪽 오른쪽 분리 (인덱스로)
  left = [0,1,2,3][:g][::-1] # 역순으로 슬라이싱해서 루프 돌리기 편하게 함
  right = [0,1,2,3][g+1:]

  base = gear[g]
  dir = base_dir
  # base 기준 왼쪽
  for idx in left:
    if moveable(base, gear[idx], 1):
      base = gear[idx]
      dir = dir * -1
      rotation[idx] = (1, dir)
    else:
      break

  # base기준 오른쪽
  base = gear[g]
  dir = base_dir
  for idx in right:
    if moveable(base, gear[idx], -1):
      base = gear[idx]
      dir = dir * -1
      rotation[idx] = (1, dir)
    else:
      break

  new_gear = []
  for idx, g in enumerate(gear):
    if rotation[idx][0]:
      new_gear.append(rotate(g, rotation[idx][1]))
    else:
      new_gear.append(g)

  return new_gear

def sol(gear, K, move):
  for i in range(K):
    gear = iteration(gear, move[ i])

  return gear[0][0] + 2 * gear[1][0] + 4 * gear[2][0] + 8 * gear[3][0]


gear, K, move = InputData()
print(sol(gear, K, move))
profile
더 나은 세상을 만들고 싶은 인공지능 개발자
post-custom-banner

0개의 댓글