공부/알고리즘 문제풀이 기록

[백준 1737] Pibonacci

도리언 옐로우 2024. 3. 30. 02:05
 

1737번: Pibonacci

첫째 줄에 P[n]을 출력한다. 값이 매우 커질 수 있으므로 1,000,000,000,000,000,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

알고리즘 : DP

난이도 : 골드Ⅳ

 

1. 문제요약

  • Fibonacci 수열을 변형한 Pibonacci 수열은 다음과 같다.
    • P[n] = 1 (0 ≤ n ≤ π)
    • P[n] = P[n-1] + P[n-π] (그 외)
  • 1000이하의 자연수 n이 주어질 때, P[n]을 출력하는 프로그램을 작성한다. 다만 값이 너무 커지는 것을 방지하기 위해 1000,000,000,000,000,000로 나눈 나머지를 출력하도록 한다.

2. 아이디어

  • 예를들어 P[10] = P[9] + P[10 - π]이고, P[10 - π] = P[9 - π] + P[10 - π*2]이며, P[10 - π*2] = P[9 - π*2] + P[10 - π*3]이고 여기서 P[10 - π*3] = 1이다.
  • P[10]을 구하기 위해 필요한 값들 중 P[9], P[9 - π], P[9 - π*2] 는 P[9]를 구할때 필요한 값이므로 DP를 위한 점화식을 만들때는 이 값들은 이미 알고 있다고 가정해도 된다.
  • 즉, P[n]을 구하기 위해서는 P[n - π], P[n - π*2], ... ,P[n - π*k]의 k개의 값을 추가로 알아야 한다. (여기서 k는 n을 π로 나눈 몫이다.)
  • 따라서 주어진 n에 대하여 n+1의 크기인 리스트 pibo를 만들고, 이 리스트의 i번째 원소는 다시 int[ i / π ] + 1의 크기를 갖는 리스트가 되도록 자료구조를 만든다. 이 자료구조를 DP로 채워나가면 구하는 답을 얻을 수 있다.
    (n = 10이면 리스트 pibo는 [[1], [1], [1], [1], [2, 1], [3, 1], [4, 1], [6, 2, 1], [9, 3, 1], [13, 4, 1], [19, 6, 2, 1]]이 된다.)

3. 구현과정

  • math 라이브러리로부터 pi를 import하여 사용한다.
  • n을 입력받고, 크기 n+1, i번째 원소는 길이 int(i/pi)+1인 리스트인 리스트 pibo를 만들고 0으로 채워준다.
  • pibo[0][-1]에 1을 저장한다. 이 값은 P[0]을 의미한다.
  • 이제 0부터 n까지 i로 순회하면서 pibo리스트를 채워줘야 한다.
  • 우선 pibo[i][-1]에 1을 저장한다. 이 값은 P[n]에서 π를 뺄 수 있을만큼 뺀 P[n - π*k]의 값을 의미한다.
  • 이제 pibo의 i번째 원소인 pibo[i] 리스트를 채워줘야 한다.
  • pibo[i][-1]에는 이미 1을 저장했으므로 0부터 len(pibo[i]) - 2까지 순회하면서 채워주면 되는데, 뒤에서부터 채울 수 있도록 revered를 취해 순회한다.
  • 가장 중요한 점화식 관계는 pibo[i][j] = pibo[i - 1][j] + pibo[i][j + 1]가 된다.
  • 이중 for문으로 pibo 리스트를 전부 채웠다면 pibo[n][0]이 구하는 답이 된다.

4. 파이썬 코드

from math import pi

n = int(input())
pibo = [[0] * (int(i / pi) + 1) for i in range(n + 1)]
pibo[0][-1] = 1
for i in range(n + 1):
    pibo[i][-1] = 1
    for j in reversed(range(len(pibo[i]) - 1)):
        pibo[i][j] = (pibo[i - 1][j] + pibo[i][j + 1]) % 10**18
print(pibo[n][0])