728x90
📄 문제개요
- 정수 X가 주어질 때 정수 X에 사용할 수 있는 연산은 다음과 같이 4가지 이다.
- X가 5로 나누어 떨어지면 5로 나눈다.
- X가 3으로 나누어 떨어지면 3으로 나눈다.
- X가 2로 나누어 떨어지면 2로 나눈다.
- X에서 1을 뺀다.
- 정수 X가 주어졌을 때, 연산 4개를 적절히 사용해서 1을 만드려고 한다. 연산을 사용하는 횟수의 최소값을 출력하시오.
🤔 문제분석
- 다이나믹 프로그래밍 문제이다.
- 특정 숫자에서 5로 나누었을때, 3으로 나누었을때, 2로 나누었을때, 1을 뺏을때 중 최적의 경로가 정답이 된다.
- X의 숫자가 주어지고, 탑다운 방식으로 5로 나누었을때, 3으로 나누었을때, 2로 나누었을때, 1로 뺐을때 중 으로 분기시켜, X가 1이 되었을때, 가장 연산이 작은 것을 골라서 정답을 도출 할 수 있다.
- 바텀업 방식에서는 1로 시작하여, 1에 5를 곱하고, 1에 3을 곱하고, 1에 2를 곱하고, 1을 더하는 방식으로 1에서 X까지 도달 될 수 있는 최적의 경우의 수를 구한다.
- 너비우선탐색으로 문제를 해결 할 수 있다.
- 반복문을 활용하여 문제를 해결 할 수 있다.
📝 의사코드
반복문을 활용한 문제 풀이
- 1부터 X까지 순회하는 반복문을 생성한다.
- 1은 0이고, 1에서 갈수있는 경우의수 곱하기5, 곱하기3, 곱하기2, 더하기 1의 최소값을 갱신한다.
- 여기서 min을 사용하는 이유는 6을 예로 들을 수 있는데 3을 예로 들을 수 있는데, 3은 1*3에서 3으로 오는경우와 1+1+1을 해서 오는경우가 존재하기때문에 항상 최소값을 선택해주어야한다.
- dp[i5] = min(dp[i]+1, dp[i5])
- dp[i3] = min(dp[i]+1, dp[i3])
- dp[i2] = min(dp[i]+1, dp[i2])
- dp[i+1] = min(dp[i]+1, dp[i+1])
너비우선탐색을 이용한 문제 풀이
- 큐에 X의 숫자와 방문 카운트를 넣는다. queue.append((X, 0))
- 큐를 하나씩 꺼내어 5로 나눌수 있다면 5로 나누고, 3을 나눌수 있다면 3을 나누고, 2를 나눌수 있다면 2로 나누고 1을 뺄수 있다면 빼서 큐에 넣는다.
- X가 1이 도달되면 그 경로는 항상 최적의 경로임으로 반복문을 탈출하고 종료한다.
💻 코드
반복문을 활용한 문제 풀이
import sys
input = sys.stdin.readline
INF = int(1e9)
X = int(input())
dp = [INF for _ in range(X+1)]
dp[1] = 0
for i in range(1, X+1):
if X >= i*5: # 5를 곱한다.
dp[i*5] = min(dp[i] + 1, dp[i*5])
if X >= i*3:
dp[i*3] = min(dp[i] + 1, dp[i*3])
if X >= i*2:
dp[i*2] = min(dp[i] + 1, dp[i*2])
if X >= i+1:
dp[i+1] = min(dp[i] + 1, dp[i+1])
print(dp[X])
너비우선탐색을 활용한 문제 풀이
import sys
from collections import deque
input = sys.stdin.readline
# BFS로 문제를 풀어보자
X = int(input())
visited = [False for _ in range(X+1)]
queue = deque()
queue.append((X, 0)) # X를 시작점으로하고 타깃이 1이다.
visited[X] = True
while queue:
x, cost = queue.popleft()
if x == 1:
print(cost)
break
if x % 5 == 0 and not visited[x//5]: # X를 5로 나눌수 있다면 5로 나눈다.
visited[x//5] = True
queue.append((x//5, cost + 1))
if x % 3 == 0 and not visited[x//3]: # X를 3으로 나눌수 있다면 3으로 나눈다.
visited[x//3] = True
queue.append((x//3, cost + 1))
if x % 2 == 0 and not visited[x//2]: # X를 2으로 나눌수 있다면 2으로 나눈다.
visited[x//2] = True
queue.append((x//2, cost + 1))
if x - 1 >= 1 and not visited[x-1]: # X를 2으로 나눌수 있다면 2으로 나눈다.
visited[x-1] = True
queue.append((x-1, cost + 1))
🎯 피드백 및 개선사항
- 너비우선탐색 vs 반복문을 활용한 문제풀이
- 숫자가 매우 커질경우, 너비우선탐색의 시간복잡도가 빠를 것이다.
- 5를 곱하면서 기하급수적으로 숫자에 빠르게 접근하기때문에 큰수일 수록 훨씬 빠를것이라고 생각된다.
❓다른사람은 어떻게 풀었을까?
- 바톰업 방식을 활용하여 문제 풀이를 확인하였습니다.
728x90
'알고리즘' 카테고리의 다른 글
[백준] 17822번 : 원판 돌리기 (0) | 2024.03.16 |
---|---|
[백준] 2174번 : 로봇 시뮬레이션 (0) | 2024.02.02 |
[알고리즘 - 이론] 분기한정법 (0) | 2023.07.25 |
파이썬 공간복잡도 (0) | 2023.06.24 |
파이썬 시간복잡도 (0) | 2023.06.24 |