BOJ
[BOJ] 12852. 1로 만들기 2
rubato.dev
2025. 7. 17. 16:33
📌 문제 제목
문제 링크: BOJ 12852
🗒️ 문제 설명
정수 X에 사용할 수 있는 연산
- X가 3으로 나누어 떨어지면, 3으로 나눈다.
- X가 2로 나누어 떨어지면, 2로 나눈다.
- 1을 뺀다.
세 개의 연산을 사용해 정수 N을 1로 만들려고 할 때, 연산을 사용하는 횟수의 최솟값을 구하여라.
출력에는 연산을 하는 횟수의 최솟값과 N을 1로 만드는데 포함되어있는 수를 공백으로 구분해서 출력
시간 제한: 0.5초
메모리 제한: 512MB
1 <= N <= 10^6 (1,000,000)
💡 문제 해결 아이디어
- BFS와 DP를 사용하여 해결 가능
# BFS 사용
- dist[i]는 N을 i로 만들려고 할 때, 연산의 최솟값.
- prev[i]는 N을 i로 만들려고 할 때, i 직전에 들러야 하는 값
# DP 사용
- 테이블 정의
- dp[i]는 1을 i로 만들려고 할 때, 연산의 최솟값
- prev[i]는 1을 i로 만들려고 할 때, i 직전에 들러야 하는 값
- 점화식 찾기
- min 사용 못함
- min을 사용하면 최솟값은 알 수 있지만, 어디에서 이 경로로 왔는지 알 수 없음.
- 1칸 빼기
- dp[i] = dp[i-1] + 1
- prev[i] = i-1
- 2로 나누어 떨어짐
- dp[i] = dp[i//2] + 1
- prev[i] = i // 2
- 3으로 나누어 떨어짐
- dp[i] = dp[i//3] + 1
- prev[i] = i // 3
- min 사용 못함
⌛️ 시간 복잡도
- O(N)
✅ 최종 코드
import sys
from collections import deque
input = sys.stdin.readline
n = int(input().strip())
visited = [False] * (n + 1)
prev = [0] * (n + 1)
queue = deque()
queue.append(n)
visited[n] = True
while queue:
cur = queue.popleft()
if cur == 1:
break
for nxt in [cur // 3, cur // 2, cur - 1]:
if (cur % 3 == 0 and nxt == cur // 3) or (cur % 2 == 0 and nxt == cur // 2) or nxt == cur - 1:
if not visited[nxt]:
visited[nxt] = True
prev[nxt] = cur
queue.append(nxt)
path = []
cur = 1
while cur != 0:
path.append(cur)
cur = prev[cur]
path.reverse()
print(len(path) - 1)
print(" ".join(map(str, path)))
# use DP
import sys
input = sys.stdin.readline
n = int(input().strip())
dp = [0] * (n + 1)
prev = [0] * (n + 1)
for i in range(2, n + 1):
dp[i] = dp[i - 1] + 1
prev[i] = i - 1
if i % 2 == 0 and dp[i // 2] + 1 < dp[i]:
dp[i] = dp[i // 2] + 1
prev[i] = i // 2
if i % 3 == 0 and dp[i // 3] + 1 < dp[i]:
dp[i] = dp[i // 3] + 1
prev[i] = i // 3
print(dp[n])
path = []
cur = n
while cur != 0:
path.append(cur)
if cur == 1:
break
cur = prev[cur]
print(prev)
print(*path)