ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 9019번 DSLR
    Algorithm Study/Python 2021. 9. 15. 22:17

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

     

    9019번: DSLR

    네 개의 명령어 D, S, L, R 을 이용하는 간단한 계산기가 있다. 이 계산기에는 레지스터가 하나 있는데, 이 레지스터에는 0 이상 10,000 미만의 십진수를 저장할 수 있다. 각 명령어는 이 레지스터에

    www.acmicpc.net

     

    문제

    네 개의 명령어 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로 변환하기 위해 필요한 최소한의 명령어 나열을 출력한다. 가능한 명령어 나열이 여러가지면, 아무거나 출력한다.

     

     

    풀이

    def D(n:int) -> int:
        temp = (n * 2) % 10000
        return temp
    
    
    def S(n:int) -> int:
        temp = (n + 9999) % 10000
        return temp
    
    
    def L(n: int) -> int:
        numbers = deque(map(int, str(n)))
        numbers.append(numbers.popleft())
        temp = int(''.join(map(str, numbers)))
        return temp
    
    
    def R(n: int) -> int:
        numbers = deque(map(int, str(n)))
        numbers.appendleft(numbers.pop())
        temp = int(''.join(map(str, numbers)))
        return temp

    D는 2배 증가시킨 후 % 10000하는 것으로 rotate할 수 있게 구현하였다.

    S는 9999를 더하고 & 10000하는 것으로 -1하는 효과를 줬다. 파이썬은 음수 %가 정상적으로 동작하지만
    C의 경우는 -1 + 10000 % 10000 해야하기 때문에 9999를 더해주는 것으로 변경했다.

    L은 deque를 이용하여 맨 왼쪽 값을 빼서 맨 오른쪽에 넣는 것으로 구현했다.

    R은 deque를 이용하여 맨 오른쪽 값을 빼서 맨 왼쪽에 넣는 것으로 구현했다.

     

     while dq_size:
                now, p = dq.popleft()
                path.append(p)
                dq_size -= 1
                
    
                if flag is False:
                    dq.append((D(now), 'D'))
                    dq.append((S(now), 'S'))
                    dq.append((L(now), 'L'))
                    dq.append((R(now), 'R'))

    그 후 BFS를 이용하여 탐색한다.

     

    if now == end:
    	idx = len(path) - 1
    	while idx > 0:
    		print(path[idx], end='')
    		idx = idx // 4
    	print()
    	flag = True
    	break

    now의 값이 목표한 end와 동일한 경우 path리스트에 넣은 값을 역순으로 찾아 경로를 구했다.
    4가지로 분기되기 때문에 //4 한 값이 부모의 idx가 된다.

     

    시간 초과가 나왔다. 동일한 숫자를 방문하는 경우 처리를 하지 않아서 그런 것 같다.

     

    visited = [0 for i in range(10000)]
    
     while dq:
     	now, p = dq.popleft()
     	
     	if now == end:
     		print(p)
     		break
    
    
    	temp = D(now)
    	if visited[temp] == 0:
    		visited[temp] = 1
    		dq.append((temp, p + 'D'))
    
    	temp = S(now)
    	if visited[temp] == 0:
    		visited[temp] = 1
    		dq.append((temp, p + 'S'))
    
    	temp = L(now)
    	if visited[temp] == 0:
    		visited[temp] = 1
    		dq.append((temp, p + 'L'))
    
    	temp = R(now)
    	if visited[temp] == 0:
    		visited[temp] = 1
    		dq.append((temp, p + 'R'))

    visited 리스트를 생성하여 이미 방문한 숫자는 queue에 추가하지 않게 수정하였다.
    path도 계산하지 않고 

     

    이번에도 시간초과가 나왔다.
    함수 호출하는 시간을 줄이기 위해서 바로 연산하고 L, R 연산을 단순하게 변경했다.

     

    temp = int(now % 1000 * 10 + now / 1000)
            if temp == end:
                print(p + 'L')
                break
            if visited[temp] == 0:
                visited[temp] = 1
                dq.append((temp, p + 'L'))
    
            temp = int(now % 10 * 1000 + now // 10)
            if temp == end:
                print(p + 'R')
                break
            if visited[temp] == 0:
                visited[temp] = 1
                dq.append((temp, p + 'R'))

    L인 경우 % 1000에 10배를 하여 /1000한 몫을 더하면 왼쪽으로 이동된다.
    R인 경우 % 10에 1000배를 하고 10으로 나눈 몫을 더하면 오른쪽으로 이동된다.

     

    통과는 했지만 시간이 너무 오래 걸려서 더 줄일 방법을 고민하였다.

     

     

     

    전체 코드

    import sys
    from collections import deque
    
    
    T = int(sys.stdin.readline().rstrip())
    for test_case in range(1, T+1):
        start, end = map(int, sys.stdin.readline().split())
        visited = [0 for i in range(10000)]
    
        dq = deque()
        dq.append((start, ""))
    
        while dq:
            now, p = dq.popleft()
            if now == end:
                print(p)
                break
    
            temp = (now * 2) % 10000
            if temp == end:
                print(p + 'D')
                break
            if visited[temp] == 0:
                visited[temp] = 1
                dq.append((temp, p + 'D'))
    
            temp = (now + 9999) % 10000
            if temp == end:
                print(p + 'S')
                break
            if visited[temp] == 0:
                visited[temp] = 1
                dq.append((temp, p + 'S'))
    
            temp = int(now % 1000 * 10 + now / 1000)
            if temp == end:
                print(p + 'L')
                break
            if visited[temp] == 0:
                visited[temp] = 1
                dq.append((temp, p + 'L'))
    
            temp = int(now % 10 * 1000 + now // 10)
            if temp == end:
                print(p + 'R')
                break
            if visited[temp] == 0:
                visited[temp] = 1
                dq.append((temp, p + 'R'))

    now가 목표값인 end와 동일한 경우 break하는 방식은 중간에 답을 만들어도 dq에서 end값이 나올 때까지,
    즉 지금까지 queue에 들어간 모든 값이 나오고 난 뒤 break된다.

    연산한 값이 end인 경우 바로 break하는 방식으로 시간을 더 줄일 수 있었다.

     

    방문 확인 break 지점 연산 방법등 다양하게 고려해야 시간제한을 만족시킬 수 있는 문제였다.
    경로를 backtracking으로 찾는 방식을 이용하면 메모리 사용량도 더 줄일 수 있을 것 같다.

    'Algorithm Study > Python' 카테고리의 다른 글

    [백준] 18428번 감시 피하기  (0) 2021.09.23
    [백준] 14889 스타트링크  (0) 2021.09.22
    [백준] 1057번 토너먼트  (0) 2021.09.11
    [백준] 1946번 신입사원  (0) 2021.09.10
    [백준] 9935번 문자열 폭발  (0) 2021.09.09

    댓글

From BlackHair