BOJ

[BOJ] 1463. 1로 만들기

rubato.dev 2025. 7. 15. 14:37

📌 문제 제목

문제 링크: BOJ 1463

🗒️ 문제 설명

정수 X가 할 수 있는 연산

  • X가 3으로 나누어 떨어지면 3으로 나눈다.
  • X가 2로 나누어 떨어지면 2로 나눈다.
  • 1을 뺀다.

정수 N을 1로 만들려고 할 때, 연산의 최솟값을 구하여라.

시간 제한: 1.5초

메모리 제한: 128MB

1 <= N <= 10^6 (1,000,000)


💡 문제 해결 아이디어

  • N이 최대 1,000,000이기 때문에 시간복잡도가 O(N)이하의 알고리즘을 사용
  • 연산 횟수를 저장할 N+1 길이의 배열을 만들어서 사용
  • BFSDP를 사용하면 풀 수 있다.

# BFS 사용

  • -1을 가지는 N+1 길이의 dist 배열 정의
  • dist[N] = 0으로 초기화
  • queue에 N을 넣고 BFS 수행
  • queue를 popleft()해서 xp를 구한 후 위의 세 연산을 수행한 값이 integer라면 dist[res] = dist[xp] + 1
  • BFS는 xp가 1일 때까지 수행

 

# DP 사용

  • 테이블 정의
    • -1을 가지는 N+1 길이의 dist 배열 정의
  • 점화식 찾기
    • n이 3으로 나누어 떨어지면 cnt[n] = cnt[n // 3] + 1
    • n이 2로 나누어 떨어지면 cnt[n] = cnt[n // 2] + 1
    • 1을 뺀 값 cnt[n] = cnt[n-1] + 1
    • 위의 세 값 중 가장 작은 값이 cnt[n]이 된다.
  • 초기값 정하기
    • dist[1] = 0

⌛️ 시간 복잡도

  • O(N)

✅ 최종 코드

시간 복잡도는 같지만 BFS가 더 빨랐음.

이해하기에도 BFS가 더 좋은듯

# use BFS

import sys
from collections import deque
input = sys.stdin.readline

N = int(input().strip())

dist = [-1] * (N + 1)
dist[N] = 0

queue = deque([N])
while queue:
    xp = queue.popleft()
    if xp == 1:
        break
    for res in [xp/3, xp/2, xp-1]:
        if int(res) != res:
            continue
        res = int(res)
        if dist[res] == -1:
            dist[res] = dist[xp] + 1
            queue.append(res)

print(dist[1])
# use DP

import sys
input = sys.stdin.readline

N = int(input().strip())

dist = [-1] * (N+1)
dist[1] = 0

for i in range(2, N+1):
    d3, d2, d1 = float("inf"), float("inf"), dist[i-1] + 1

    if i % 3 == 0:
        d3 = dist[i // 3] + 1
    if i % 2 == 0:
        d2 = dist[i // 2] + 1
    dist[i] = min(d3, d2, d1)
print(dist[N])