문제
기록 포인트
- 동적 프로그래밍 개념을 문제에 적용하는 과정
- 부분 문제 정의하기
- 점화식 만들기, 부분 문제의 개수 세기, 각 문제의 선택지 개수 세기
- 메모 테이블 구성하기
- 부분 문제의 선후관계(topological order)를 고려해 순서대로 실행하기
- 하향식 vs 상향식
- 하향식: 현재 문제를 처리하기 위해 부분 문제로 나누어 재귀적으로 내려가는 방식
- 상향식: 위상적 순서에 따라 부분 문제들을 해결해 올라오는 방식
- (오름차순/내림차순의 차이가 아니라, 부분 문제들을 푸는 순서의 차이)
접근 방식
- 하향식
- [1안] 하위 문제 f(x): x에서 N까지 이동하기 위한 최소 횟수
- [2안] 하위 문제 g(x): x에서 1까지 이동하기 위한 최소 횟수
- 상향식
- [1안] 하위 문제 f'(x): N에서 x까지 이동하기 위한 최소 횟수
- [2안] 하위 문제 g'(x): 1에서 x까지 이동하기 위한 최소 횟수
- 상향식으로 풀 때 각 정점에서 본인에게 들어오는 부분 문제들을 확인할 수도 있고, 반대로 각 부분 문제에서 다음 단계의 문제들로 정보를 넘겨줄 수도 있음
제출 답안
import sys
N = int(sys.stdin.readline())
memo = [None] * (N+1)
def fn_topdown(x):
if memo[x]:
return memo[x]
if x == 1:
return 0
subproblems = []
if x%3 == 0: subproblems.append(x//3)
if x%2 == 0: subproblems.append(x//2)
if x > 1: subproblems.append(x-1)
subscores = []
for p in subproblems:
subscores.append(fn_topdown(p))
memo[x] = 1 + min(subscores)
return memo[x]
def fn_topdown2(x):
if memo[x]:
return memo[x]
if x == 1:
return 0
min_subscore = float("inf")
if x%3 == 0:
min_subscore = min(min_subscore, fn_topdown2(x//3))
if x%2 == 0:
min_subscore = min(min_subscore, fn_topdown2(x//2))
if x > 1:
min_subscore = min(min_subscore, fn_topdown2(x-1))
memo[x] = 1 + min_subscore
return memo[x]
from collections import deque
def fn_bottomup(x):
memo = [None] * (N+1)
memo[1] = 0
frontier = deque([1])
while not memo[x] and frontier:
v = frontier.popleft()
subscore = memo[v]
for next_v in [v*3, v*2, v+1]:
if next_v == x:
memo[next_v] = subscore + 1
break
elif next_v < N+1:
memo[next_v] = subscore + 1
frontier.append(next_v)
return memo[x]
def fn_bottomup2(x):
memo = [None] * (N+1)
memo[1] = 0
for i in range(2, N+1):
min_subscore = float("inf")
if memo[i-1] != None:
min_subscore = min(min_subscore, memo[i-1])
if i%2 == 0 and memo[i//2] != None:
min_subscore = min(min_subscore, memo[i//2])
if i%3 == 0 and memo[i//3] != None:
min_subscore = min(min_subscore, memo[i//3])
memo[i] = min_subscore + 1
return memo[x]
print(fn_bottomup2(N))