공부하자!/알고리즘

백준 | 9461 파도반 수열 Swift - DP

지우개원정대 2023. 7. 18. 15:53

이전 게시글에서 다이나믹 프로그래밍을 맛볼 수 있었는데,

한 발짝 더 나아가서 효율적으로 풀 수 있는 방법을 고민하는 문제가 여기 있다.

 

이전 게시글 보러가기 ⬇️

2023.07.18 - [공부하자!/알고리즘] - 백준 | 9095 1, 2, 3 더하기 Swift - DP

 

우선, 점화식을 구해보자!

문제에서 P(1)부터 P(10)까지 제공해주었다

1 2 3 4 5 6 7 8 9 10
1 1 1 2 2 3 4 5 7 9

그림에서 보면 알 수 있듯이,

11번째 삼각형의 한 변의 길이는 9+3, 즉 P(10)+P(6)인 12이다.

10번째 삼각형의 한 변의 길이는 P(9)+P(5)인 9이다.

즉 점화식을 구하면 다음과 같다

P(n) = P(n-1) + P(n-5)

 

하지만 이걸 그대로 코드로 구현하면 시간 초과가 뜬다!

주어지는 N의 범위가 <=100이기 때문에, P(100)을 하기 위해 수없이 많은 과정이 필요하기 때문!

P(100)
= P(99) + P(95)
= P(98) + P(94) + P(94) + P(90)
= P(97) + P(93) + P(93) + P(89) + P(93) + P(89) + P(89) + P(85) ...

숫자가 커질수록 중복되는 과정도 매우 많고,

재귀적으로 구현했을 때 함수 P를 불러오는 횟수가 매우매우 많아지기 때문에 더 좋은 방법을 생각해내야 한다.

 

여기에서 추가해볼 수 있는 기능은 바로 '저장해두기'!!!

만약 P(10)을 처음으로 구했을 때 어딘가에 저장해두었다면, 다시 P(10)을 위해 P(9)와 P(5)를 구하지 않아도 되도록 만드는 것이다!

이를 구현하기 위해 딕셔너리를 사용했다.

 

let t = Int(readLine()!)!

// P 함수 구현 후 구한 값을 저장해두는 딕셔너리
var pDict = [1:1, 2:1, 3:1, 4:2, 5:2]

// P 함수 구현
func P(_ n: Int) -> Int {
    var answer = 0
    
    if pDict[n] != nil { 	// pDict에 값이 있다면 바로 출력
        answer = pDict[n]!
    } else { 			// pDict에 값이 없다면 구한 후 출력
        pDict[n] = P(n-1) + P(n-5)
        answer = pDict[n]!
    }
    
    return answer
}

// 입출력 실행 부분
for _ in 0..<t {
    let n = Int(readLine()!)!
    print(P(n))
}