라떼는말이야

[solved.ac 골드4] 9019_DSLR (파이썬, BFS) 본문

알고리즘/코딩 테스트

[solved.ac 골드4] 9019_DSLR (파이썬, BFS)

MangBaam 2022. 4. 8. 05:40
반응형

 

제한 사항

문제

네 개의 명령어 D, S, L, R 을 이용하는 간단한 계산기가 있다. 이 계산기에는 레지스터가 하나 있는데, 이 레지스터에는 0 이상 10,000 미만의 십진수를 저장할 수 있다. 각 명령어는 이 레지스터에 저장된 n을 다음과 같이 변환한다. n의 네 자릿수를 d1, d2, d3, d4라고 하자(즉 n = ((d1 × 10 + d2) × 10 + d3) × 10 + d4라고 하자)

  1. D: D 는 n을 두 배로 바꾼다. 결과 값이 9999 보다 큰 경우에는 10000 으로 나눈 나머지를 취한다. 그 결과 값(2n mod 10000)을 레지스터에 저장한다.
  2. S: S 는 n에서 1 을 뺀 결과 n-1을 레지스터에 저장한다. n이 0 이라면 9999 가 대신 레지스터에 저장된다.
  3. L: L 은 n의 각 자릿수를 왼편으로 회전시켜 그 결과를 레지스터에 저장한다. 이 연산이 끝나면 레지스터에 저장된 네 자릿수는 왼편부터 d2, d3, d4, d1이 된다.
  4. R: R 은 n의 각 자릿수를 오른편으로 회전시켜 그 결과를 레지스터에 저장한다. 이 연산이 끝나면 레지스터에 저장된 네 자릿수는 왼편부터 d4, d1, d2, d3이 된다.

위에서 언급한 것처럼, L 과 R 명령어는 십진 자릿수를 가정하고 연산을 수행한다. 예를 들어서 n = 1234 라면 여기에 L 을 적용하면 2341 이 되고 R 을 적용하면 4123 이 된다.

여러분이 작성할 프로그램은 주어진 서로 다른 두 정수 A와 B(A ≠ B)에 대하여 A를 B로 바꾸는 최소한의 명령어를 생성하는 프로그램이다. 예를 들어서 A = 1234, B = 3412 라면 다음과 같이 두 개의 명령어를 적용하면 A를 B로 변환할 수 있다.

1234 →L 2341 →L 3412
1234 →R 4123 →R 3412

따라서 여러분의 프로그램은 이 경우에 LL 이나 RR 을 출력해야 한다.

n의 자릿수로 0 이 포함된 경우에 주의해야 한다. 예를 들어서 1000 에 L 을 적용하면 0001 이 되므로 결과는 1 이 된다. 그러나 R 을 적용하면 0100 이 되므로 결과는 100 이 된다.

입력

프로그램 입력은 T 개의 테스트 케이스로 구성된다. 테스트 케이스 개수 T 는 입력의 첫 줄에 주어진다. 각 테스트 케이스로는 두 개의 정수 A와 B(A ≠ B)가 공백으로 분리되어 차례로 주어지는데 A는 레지스터의 초기 값을 나타내고 B는 최종 값을 나타낸다. A 와 B는 모두 0 이상 10,000 미만이다.

출력

A에서 B로 변환하기 위해 필요한 최소한의 명령어 나열을 출력한다. 가능한 명령어 나열이 여러가지면, 아무거나 출력한다.

예제


나의 풀이

문제의 접근 자체는 어렵지 않았다. 하지만 시간, 메모리 제한이 매우 빡세게 잡혀 있는지 한 시간을 고생했다.

 

DSLR 함수

def calc(cmd, num):
    if cmd == 0:  # D
        return (num * 2) % 10000
    elif cmd == 1:  # S
        return (num - 1) % 10000
    elif cmd == 2:  # L
        return (num % 1000) * 10 + num // 1000
    elif cmd == 3:  # R
        return (num % 10) * 1000 + num // 10

문제에서 설명한 D, S, L, R 연산에 대한 함수이다.

L과 R의 경우 str으로 바꾼 후에 작업 후 int로 변환해서 return 할 수도 있지만 이 문제에서 시간 제한이 아주 빡세게 잡혀 있어서 int -> str -> int 과정에 시간 손해가 많을 것이다. 다행히 간단한 수식으로 처리할 수 있다.

BFS 함수

command = 'DSLR'
visited = list()

def bfs(num, target):
    global visited
    queue = deque([(num, "")])

    while queue:
        cn, cc = queue.popleft()

        if cn == target: return cc
        for i in range(4):
            cal_num = calc(i, cn)
            if not visited[cal_num]:
                visited[cal_num] = True
                queue.append((cal_num, cc + command[i]))

visited는 해당 숫자가 등장한 적이 있는 지를 체크한다.

visited는 bfs 함수가 실행되기 전 초기화 과정이 필요하다. (곧 나올 solution 함수에서 구현함)

calc 함수를 통해 계산한 결과를 cal_num에 담고, 방문하지 않았다면 방문 처리 후 Queue에 cal_num과 어떤 연산이 수행되었는지를 저장한다.

이때 앞선 연산 뒤에 이번의 연산을 붙여서 저장해야 이전의 연산들을 기록할 수 있다. ( cc + command[i] )

이 때 command에 정의된 'DSLR' 알파벳 순서와 calc 함수의 cmd가 의미하는 것이 일치해야 하는 점을 주의해야 한다.

 

로직

for _ in range(int(input())):
    n, t = map(int, input().split())
    visited = [False] * 10000
    print(bfs(n, t))

최초에 사용자에게 입력 받은 만큼 반복한다.

두 숫자를 받은 후 visited 리스트를 초기화하고 bfs 함수를 수행한다.

문제의 입력 조건에서 숫자의 크기는 10000 이하의 자연수라고 했기 때문에 visited의 크기는 10000 으로 하면 딱 맞다.

 

채점 결과

Python3 로 풀이한 사람이 5명 밖에 안 되는 걸 보면 문제의 시간/메모리 제한이 너무 빡세게 잡혀있지 않나 생각이 든다. (그마저도 전부 코드 비공개라 어떻게 풀었나 궁금하다)

문제 자체는 어렵지 않으나 Python3로는 거의 풀지도 못 할 만큼 까다로웠다.

반응형
Comments