Programming/BOJ

[DP] 9095, 15988번 1,2,3 더하기

거북이주인장 2022. 1. 9. 13:17

https://www.acmicpc.net/problem/9095

https://www.acmicpc.net/problem/15988

알고리즘

  • Brute Force
  • Dynamic Programming

풀이

포인트

  • 같은 숫자를 여러번, 연속적으로 사용해도 된다는 점
  • 정수 n을 구성하는 숫자의 순서는 상관 없다는 점
    • 1+1+2, 1+2+1, 2+1+1은 모두 다른 경우의 수
  • 정수 n을 구성하는 숫자의 개수의 제한이 없다는 점

Brute Force

문제에서 n은 11보다 작다. 따라서 n은 최대 10개의 수로 구성될 수 있다. 그러면 각 자리마다 가능한 정수 (1,2,3)를 모두 놓아봄으로써 가능한 경우 (그 정수들의 합이 n)만 체크해주면 된다.

Brute Force로 프로그래밍할 때, 재귀를 이용하는 것을 즐겨하기 때문에 재귀를 이용하여 모든 경우를 탐색해주었다. 일 때는 문제의 조건에 충족하는 경우니까 ans에 1을 더해주고 일 때는 조건에 맞지 않는 경우이니 그냥 넘어가준다.

각 자리수마다 3개의 수를 탐색한다. 즉, 각 자리수에 1,2,3을 for문에서 재귀적으로 넣어보는 것인데 따라서 go 함수가 끝나면 더해줬던 수인 numbers[i]를 다시 빼준다. 이러한 재귀적인 과정 때문에 시간 복잡도는 이다. 최대 10자리이고 각 자리마다 세 개의 수인 1,2,3을 넣을 수 있기 때문이다.

[Source Code]

t = int(input())
def go(s, n):
  global ans
  if s == n:
      ans += 1
      return
  if s > n:
      return
  for i in range(3):
      s += numbers[i]
      go(s, n)
      s -= numbers[i]
for _ in range(t):
  n = int(input())
  numbers = [1,2,3]
  ans = 0
  go(0,n)
  print(ans)

Dynamic Programming

앞서 살펴본 Brute Force 풀이는 $n < 11$이기에 가능한 풀이이다. 즉, $n$이 조금만 더 커져도 시간초과가 뜰게 뻔하다. 15988번을 위의 Brute Force로 푼다면 복잡도는 $O(3^{1000000})$으로, 시간초과가 뜬다.

복잡도를 줄이기 위해, DP를 이용한다. 먼저, dp 테이블과 점화식을 아래와 같이 정의한다.

  • $dp[i]$ : 정수 $i$를 1,2,3을 이용하여 만드는 경우의 수
  • $dp[i] = dp[i-1] + dp[i-2] + dp[i-3]$

정수 $i$에서 1만큼 떨어진 정수인 $i-1$에서 $i$을 만들기 위해서는 1을 더해주면 되고, 2만큼 떨어진 정수인 $i-2$에서 $i$를 만들기 위해서는 2를, 3만큼 떨어진 정수인 $i-3$에서 $i$을 만들기 위해서는 3을 더해주면 된다. 각 수를 더해주는 것은 $i-j$를 만드는 경우의 수를 그대로 가져오고 $j$만 더해주는 것이다. 따라서 위의 점화식을 세울 수 있다.

DP 테이블이 $n \times 1$의 1차원이므로, 시간 복잡도는 $O(n)$이다. 따라서 $n = 1000000$ 이더라도 파이썬으로 1초 이내에 문제를 해결할 수 있다.

[Source Code]

m = 1000000
dp = [0]*(m+1)
dp[1] = 1
dp[2] = 2
dp[3] = 4
for i in range(4,m+1):
    dp[i] = (dp[i-1] + dp[i-2] + dp[i-3]) % 1000000009

n = int(input())
for _ in range(n):
    a = int(input())
    print(dp[a])