IT/ps

백준 16194번 1,2,3 더하기 5

u149_cinderella 2025. 1. 24. 00:12

1,2,3 더하기랑 비슷한데 많이 다르다

연속 2번이 불가능하다 중복이 불가능한게 아니다

 

전에 풀었던 카드구매하기랑 유사한점이 보인다

1,2,3벽돌을 이용해서 n층의 탑을 쌓아야한다

이렇게 접근 할 수 있을것같다

 

다만 이렇게 간단하게 끝나지 않고 "연속해서 등장할 수 없다"라는 점을 생각해야한다

그래서 이 접근은 잘못됐을거라고 생각했고

이런 그래프라고 생각해보기로 했다

 

1에서 출발하는거

2에서 출발하는거

3에서 출발하는거

이 세개에서 합이 n이 되는 애들을 개수를 구하면 될거같다

 

import sys

t=int(sys.stdin.readline().rstrip())

for _ in range(t):
    n=int(sys.stdin.readline().rstrip())

    v=[0]*(n+1)

    def dp(n,state=0):
        if n<0:
            return 0
        elif n==0:
            return 1
        if state==0:
            v[n] = dp(n-1,1)+dp(n-2,2)+dp(n-3,3)
        else:
            v[n] = dp(n-1,1)+dp(n-2,2)+dp(n-3,3)-dp(n-state,state)
        return v[n]
    
    print(dp(n))

그래서 코드를 이렇게 dfs느낌으로 작성했다(아직 완성된 코드가 아니다 답은 나오는데 시간초과 날거임)넣어도 상당히 오래걸린다 이렇게 작성하면 시간복잡도는 O(2^n)이다

n은 최대 10^5이므로 무조건 시간초과가 걸릴것이다

(추가로 재귀횟수 제한도 걸릴거고)

 

 

 

중복을 제거하기 위해 코드를 수정하자

def dp(n,state=0):
        if n<0:
            return 0
        elif n==0:
            return 1
        if v[n]!=0:
            return v[n]-dp(n-state,state)
        if state==0:
            v[n] = dp(n-1,1)+dp(n-2,2)+dp(n-3,3)
        else:
            v[n] = dp(n-1,1)+dp(n-2,2)+dp(n-3,3)-dp(n-state,state)
        return v[n]

수정을 좀 해봤는데 4는 맞게 나오는데 7이 틀리게나온다

예를들어 3을 이미 구한경우 저장된 3의 값-dp(3,노드번호)이런 의도로 작성했는데 정상적으로 동작하지 않았나보다

 

조금더 수정해줬다

import sys

t=int(sys.stdin.readline().rstrip())

for _ in range(t):
    n=int(sys.stdin.readline().rstrip())

    v = [[0] * 4 for _ in range(n+1)]

    def dp(n,state=0):
        if n<0:
            return 0
        elif n==0:
            return 1
        
        if v[n][state]!=0:
            return v[n][state]
        
        if state==0:
            v[n] = (dp(n-1,1)+dp(n-2,2)+dp(n-3,3))%1000000009
            return v[n]
        else:
            v[n][state] = dp(n-1,1)+dp(n-2,2)+dp(n-3,3)-dp(n-state,state)%1000000009
            return v[n][state]
        
    
    print(dp(n))

 

v를 2차원으로 만들어서 해당 숫자의 이전 state가 몇인가에 따라 몇 개인지를 저장하게끔 했다

물론 이번에는 재귀 제한에 걸리지만 조금만 더 수정하면 될거같다

(리스트 컴프리헨션 부분은 처음에 v=[[0]*(3+1)]*(n+1) 로 했다가 이렇게 만들면 얇은복사만 된다는걸 몰라서 삽질 좀 했다)

 

아무튼 답은 멀쩡히 나오지만 문제는 재귀제한에 걸린다는거다

100을 넣어도 즉시 답이 나오므로 접근 방법 자체는 이게 맞는 듯 하다.

입력제한이 10^6이므로 재귀제한을 피하려면 재귀를 n/100까지만 들어가야하는데 쉽지않을 듯 하다

(10^5인줄 알고 재귀제한 풀고 제출했다가 재귀오류 걸렸다...)

 

 

그렇다면 이걸 그대로 바텀 업으로 구현한다면 되지않을까?

 

import sys

t=int(sys.stdin.readline().rstrip())

for _ in range(t):
    n=int(sys.stdin.readline().rstrip())

    v = [[0] * 4 for _ in range(n+1)]

    v[1][1]=1
    v[2][2]=1
    v[3][1]=1
    v[3][2]=1
    v[3][3]=1

    for i in range(1,n+1):
        for j in range(1,3+1):
            if i>3:
                v[i][j]+=((v[i-j][1]+v[i-j][2]+v[i-j][3]-v[i-j][j])%1000000009)
        v[i][0]=(v[i][1]+v[i][2]+v[i][3])%1000000009
    
    print(v[n][0])

 

분명 탑다운을 바텀업으로 바꾸는건데 4시간가까이 걸렸다...

초기값 부분에서 좀 헤맸다

 

이 코드로 제출을 했더니

??? 이게 왜 시간초과야? O(n)이잖아?

 

하고 다시 코드를 잘 살펴보니 입력받을때마다 n까지 초기화 하는 방식이라서 그런것 같다 그냥 10^5까지 값을 구해놓고 바로 출력하는걸로 다시 작성하자

import sys

t=int(sys.stdin.readline().rstrip())

n=100000
v = [[0] * 4 for _ in range(n+1)]
v[1][1]=1
v[2][2]=1
v[3][1]=1
v[3][2]=1
v[3][3]=1

for i in range(1,n+1):
    for j in range(1,3+1):
        if i>3:
            v[i][j]+=((v[i-j][1]+v[i-j][2]+v[i-j][3]-v[i-j][j])%1000000009)
    v[i][0]=(v[i][1]+v[i][2]+v[i][3])%1000000009

for _ in range(t):
    in_n=int(sys.stdin.readline().rstrip())
    
    
    print(v[in_n][0])

 

총 9시간 정도 걸린것같다

 

 

그런데 오늘도 마찬가지로 다른사람들보다 2배가량 느리다 도대체 왤까

이번주까지도 이유를 자력으로 못알아내면 그냥 다른사람 풀이를 봐야겠다