알고리즘/Python

[python] 백준 1463 - 1로 만들기

제주도랏맨 2022. 3. 16. 03:03

출처 : 백준, https://www.acmicpc.net/problem/1463

 

1463번: 1로 만들기

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

www.acmicpc.net

 

더보기

 

첫 번째 풀이 : 실패

 

if __name__ == '__main__':
  n = int(input())
  count = 0

  while n != 1:
    if n % 2 == 1: #홀수
      if n % 3 == 0:
        n //= 3
      else:
        n -= 1
    else:#짝수
      if n % 3 == 0:
        n //= 3
      else:
        if (n-1) % 3 == 0:
          n -= 1
        else:
          n //= 2

    count += 1

  print(count)
125
10

#8

 

 

두 번째 풀이 : 실패

 

if __name__ == '__main__':
  n = int(input())
  count = 0

  while n != 1:
    if n % 2 == 1: #홀수
      if n % 3 == 0:
        n //= 3
      else:
        n -= 1
    else:#짝수
      if n % 3 == 0:
        n //= 3
      elif n % 3 == 1: #3n + 1인 짝수라면
        if (n // 2) % 2 == 1 and (n // 2) % 3 != 0:
          n -= 1
        else:
          n //= 2
      else:
        n //= 2

    count += 1

  print(count)
1000
10

#9

 

첫 번째 풀이와 두 번째 풀이는

n이 3의 배수이면 3으로, 짝수라면 2로, 둘 다 아니라면 -1을 1이 될 때까지 하는 방식의 변형들이다.

물론 둘 다 실패했다.

 

 

세 번째 풀이 : DP

 

if __name__ == '__main__':
  n = int(input())
  step = [0] * (n+1)

  INF = 10e6 + 5
  
  for i in range(2, n+1):
    A = step[i // 3] if i % 3 == 0 else INF
    B = step[i // 2] if i % 2 == 0 else INF
    C = step[i - 1]

    step[i] = min(A, B, C) + 1

  print(step[n])

 

대체 뭐가 문제인가 싶어서 알고리즘 분류를 봤는데 다이나믹 프로그래밍인 것을 보고 머리가 띵했다.

그리고 DP라고 생각하니 정말 금방 문제가 풀리더라.

 

일단 DP이므로 점화식을 세워보자.

X가 1이 될 때 까지의 최소 횟수 step[X]라하면,

 

step[X] = min(step[X//3], step[X//2], step[X-1]) + 1(X≥2), step[1] = 0 (단, 나누어 떨어지지 않을 경우 무한)

 

라 세울 수 있다.

1에서부터 X까지 올라간다고 생각해보면 1에서 X까지 도달하기 위한 최소 횟수는

X//3까지의 최소 횟수X//2까지의 최소 횟수X-1까지의 최소 횟수 중 최소값에 +1을 한 것이기 때문이다.


 

시간 복잡도

 

O(n)

 

 

고찰

 

왜 DP인지 모르겠는데? 왜 DP인가?

 

항상 막히는 부분이 이런건데 어떤 알고리즘을 써야하는지 몰라서 잘못된 풀이를 작성해 문제를 풀지 못하는 경우가 많다.

이를 위해서 이 문제에서 DP를 써야하는 것을 어떻게 추론할 수 있는지 적어놓고자 한다.

 

먼저 이 문제는 1까지 도달하는데 걸리는 과정의 최소 횟수를 구하는 문제이다.

여기서 '최소 횟수'라는 말이 중요한데 즉, 이는 최솟값을 구하는 최적화 문제에 해당한다.

 

대학 알고리즘 과정 중 배우는 알고리즘은 다음과 같다.

 

1. Complexity
2. Recursion
3. Divide and Conquer + Sorting
4, 완전 탐색
5. Greedy
6. Backtracking
7. DP
8. Graph : BFS, DFS, Minimun Spanning Tree, Shortest Path
9. NP

 

이 중 최적화 문제에 쓰이는 대표적인 알고리즘은 바로

 

5. Greedy
7. DP

 

이다. 그렇다면 이 문제는 최적화 문제이므로 이 2가지 알고리즘으로 먼저 접근해보아야 하는데,

Greedy는 치명적인 단점을 가지고 있는 것이 바로 현재에 선택한 최선이 결과적으로 최선이어야 한다는 점이다.

이를 바탕으로 위 문제에서 현재에 최선인 값을 뽑아보면, X값을 가장 많이 줄일수록 최선이므로

 

X가 3의 배수일 때, 3으로 나누기

X가 2의 배수일 때, 2로 나누기

X가 둘 다 아닐 때, 1만큼 빼기

 

이다. 그리고 이는 예제 중 10이 10 -> 9 -> 3 -> 1로 진행되는 점만 보아도 아니라는 것을 알 수 있다.

10에서의 최선은 2의 배수이므로 2로 나누어 5가 되는 것인데, 실제 최소 횟수는 -1을 빼 9로 가는 방법이기 때문이다.

이를 통해 이 문제는 Greedy로 푸는 문제가 아니라고 결론 낼 수 있다.

(내가 실패한 첫 번째, 두 번째 풀이가 Greedy 방식으로 풀려 시도한 것이었다.)

 

그렇다면 DP인지 체크하기 위해 점화식을 세워봐야하는데, 점화식은 아래와 같이 바로 세워지는 것을 확인할 수 있다.

 

step[X] = min(step[X//3], step[X//2], step[X-1]) + 1(X≥2), step[1] = 0 (단, 나누어 떨어지지 않을 경우 무한)

 

여기서 이 문제는 DP로 풀어야한다는 것을 확정지을 수 있는 것이다.

 

즉, 오늘과 같은 실수를 하지 않기 위해서는 

 

1. 무엇이 목표인지 먼저 파악하고 최적화 문제로 파악된다면, Greedy와 DP를 먼저 시도해본다.

2. Greedy가 쉬우므로 먼저 체크해보고, 아니거나 애매하다면 DP인지 확인하기 위해 점화식을 체크해본다.