1 minute read

백준 문제 중 9184번

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

문제

재귀 호출만 생각하면 신이 난다! 아닌가요?

다음과 같은 재귀함수 w(a, b, c)가 있다.

if a <= 0 or b <= 0 or c <= 0, then w(a, b, c) returns:
    1

if a > 20 or b > 20 or c > 20, then w(a, b, c) returns:
    w(20, 20, 20)

if a < b and b < c, then w(a, b, c) returns:
    w(a, b, c-1) + w(a, b-1, c-1) - w(a, b-1, c)

otherwise it returns:
    w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1)

위의 함수를 구현하는 것은 매우 쉽다. 하지만, 그대로 구현하면 값을 구하는데 매우 오랜 시간이 걸린다. (예를 들면, a=15, b=15, c=15)

a, b, c가 주어졌을 때, w(a, b, c)를 출력하는 프로그램을 작성하시오.

입력

입력은 세 정수 a, b, c로 이루어져 있으며, 한 줄에 하나씩 주어진다. 입력의 마지막은 -1 -1 -1로 나타내며, 세 정수가 모두 -1인 경우는 입력의 마지막을 제외하면 없다.

출력

입력으로 주어진 각각의 a, b, c에 대해서, w(a, b, c)를 출력한다.

제한

  • -50 ≤ a, b, c ≤ 50

풀이

DP 중 이미 계산한 결과값을 저장했다가 다음 계산에서 쓰는 메모이제이션을 쓰면 해결되는 문제였다.

주어진 함수의 조건식 중 if a>20 or b>20 or c>20: returns w(20, 20, 20) 을 보고 3차원 배열에 각각의 크기가 21인 배열을 쓰면 될것임을 알았다.

문제는 자체는 쉬웠으나 주의해야할 점이 있다. 입력으로는 음수들이 들어 올 수 있지만 dp의 인덱스에서는 음수를 고려하지 않았기때문에 w함수가 호출되자마자 인덱스 범위를 벗어나는 경우들은 예외처리를 상단에 위치하게 함수를 구성해야한다.


 # 메모이제이션을 활용할 3차원 배열 dp 선언
dp = [[[0 for _ in range(21)] for _ in range (21)] for _ in range (21)]

 # 메모이제이션을 활용할 함수 선언
def w(a:int, b:int, c:int)->int:

    if a<=0 or b<=0 or c<=0:
        return 1

     # a>20 or b>20 or c>20 이면 재귀적으로 w(20,20,20)를 호출한다.
    if a>20 or b>20 or c>20:
        return w(20,20,20)
        
     # 이미 계산이 되어 dp에 존재하는 값이라면 바로 리턴
    if dp[a][b][c]:
        return dp[a][b][c]

    if a<b and b<c:
        dp[a][b][c] = w(a, b, c-1) + w(a, b-1, c-1) - w(a, b-1, c)
        return dp[a][b][c]

    dp[a][b][c] = w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1)
    return dp[a][b][c]



while True:
    a, b, c = map(int, input().split()) 
    if a==-1 and b==-1 and c==-1: 
        break
    print(f'w({a}, {b}, {c}) = {w(a,b,c)}')
20 21 50
w(20, 21, 50) = 1048576
-1 -1 -1

Leave a comment