-
[백준] 9019번 DSLRAlgorithm Study/Python 2021. 9. 15. 22:17
https://www.acmicpc.net/problem/9019
문제
네 개의 명령어 D, S, L, R 을 이용하는 간단한 계산기가 있다. 이 계산기에는 레지스터가 하나 있는데, 이 레지스터에는 0 이상 10,000 미만의 십진수를 저장할 수 있다. 각 명령어는 이 레지스터에 저장된 n을 다음과 같이 변환한다. n의 네 자릿수를 d1, d2, d3, d4라고 하자(즉 n = ((d1 × 10 + d2) × 10 + d3) × 10 + d4라고 하자)
- D: D 는 n을 두 배로 바꾼다. 결과 값이 9999 보다 큰 경우에는 10000 으로 나눈 나머지를 취한다. 그 결과 값(2n mod 10000)을 레지스터에 저장한다.
- S: S 는 n에서 1 을 뺀 결과 n-1을 레지스터에 저장한다. n이 0 이라면 9999 가 대신 레지스터에 저장된다.
- L: L 은 n의 각 자릿수를 왼편으로 회전시켜 그 결과를 레지스터에 저장한다. 이 연산이 끝나면 레지스터에 저장된 네 자릿수는 왼편부터 d2, d3, d4, d1이 된다.
- 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