본문 바로가기
코딩테스트

dp - 심화 2

by 임지혁코딩 2024. 2. 27.

백준 1793

import sys

dp = [ 0 for _ in range(251)]

dp[0] = 1
dp[1] = 1
dp[2] = 3


for i in range(3,251) :
    dp[i] = dp[i-1] + (dp[i-2]*2)
while(True) :
    try :
        N= int(sys.stdin.readline())
        print(dp[N])
    except:
        break
   

 

2*N을 채우는 방법은, dp[i-1] (전 거를 채우는 방법 + 세로로 한줄) + dp[i-2]*2(3개가 아니라 2개인 이유는, 다 세로인 경우는 앞에 포함된다. 

로 쉽게 풀었다.

 

자꾸 java와 혼동하여 try catch로 하는데, try : except:임을 기억하자. 

 

 

백준 1003

 

import sys

dp = [0] * 41
dp[1] = 1

zerosum = 0
onesum = 0

def DFS(N) :

    global zerosum

    global onesum
   
    if (N == 0) :
        zerosum += 1
        return 0
    elif N == 1:
        onesum += 1
        return 1

    else :
        return DFS(N-1)+DFS(N-2)

T = int(sys.stdin.readline())

for i in range(T) :

    zerosum = 0
    onesum = 0

    DFS(int(sys.stdin.readline()))

    print(zerosum,onesum)

 

# else 후에 return이 큰 의미는 없지만, 그렇다고 빼면 한 함수에서 return이 2개로 분기되는 현상이 발생해 오류가 생긴다.

 

이와 같이 코드를 작성하였지만, 시간제한 0.25초에 따라 당연하게 시간 제한이 걸렸다. 

 

백준 1003번

import sys

dp = [[0,0] for _ in range(41)]
dp[0] = [1,0]
dp[1] = [0,1]

T = int(sys.stdin.readline())

for i in range(T) :

    zerosum = 0
    onesum = 0

    N= int(sys.stdin.readline())

    for j in range(2,N+1):
        dp[j][0] = dp[j-1][0]+dp[j-2][0]
        dp[j][1] = dp[j-1][1]+dp[j-2][1]
   
    print(dp[N][0],dp[N][1])

 

0과 1의 호출 수를 dp에 저장하여, 이를 return하는 형태로 구현하였다. 

 

백준 9465 

import sys


T =int(sys.stdin.readline())

for i in range(T) :
    N = int(sys.stdin.readline())
    Narr = [ list(map(int,sys.stdin.readline().split())) for _ in range(2) ]

    dp = [ [0] * N for _ in range(2) ]

    if N == 1 :
        print(max(Narr[0][0],Narr[1][0]))
        continue
    elif N == 2:
        print(max(Narr[0][0]+Narr[1][1],Narr[1][0]+Narr[0][1]))
        continue
    else:
        dp[0][0] = Narr[0][0]
        dp[1][0] = Narr[1][0]
        dp[0][1] = Narr[1][0]+Narr[0][1]
        dp[1][1] = Narr[0][0]+Narr[1][1]

       
        for i in range(2,N) :
            dp[0][i]= max(dp[1][i-2],dp[1][i-1])+Narr[0][i]
            dp[1][i]= max(dp[0][i-2],dp[0][i-1])+Narr[1][i]

        print(max(dp[0][-1],dp[1][-1]))

 

DP문제를 풀때, 전 값이 어디서 나오는지를 고민하는 것 까진 잘 했다.

하지만 DP 문제의 최댓값을 풀때는 주로

DP[I][X] = MAX(이전에서 올지, 어디서올지) + NARR[I][X]임을 다시 기억하면 좋을 것 같다. 

 

해당 경우는 DP를 채우는 경우의 수가 2가지가 있엇으며,

처음과 두번쨰의 경우엔 해당 방식으로 채울수가 없기도 했다. 

 

백준 1890

import sys

N = int(sys.stdin.readline())

Narr = []

for i in range(N) :
    Narr.append(list(map(int,sys.stdin.readline().split())))

dp = [ [0]*N for _ in range(N) ]

cnt = 0

def DFS(x,y) :

    global cnt
    if x < 0 or y < 0 or x >= N or y >= N:
        return

    if Narr[x][y] == 0:
        cnt += 1
        return

    else :
        DFS(x+Narr[x][y],y)
        DFS(x,y+Narr[x][y])
        return

DFS(0,0)
print(cnt)

 

경로를 계산하는 것은, 항시 dfs 로 해왔기 떄문에 dfs를 하였지만, 이는 너무 많은 시간 복잡도가 생겼다.

O ( 2 의 100승 ) .. 

 

<경로의 수를 계산하는 것을, dp로 구현해보자>

import sys
sys.setrecursionlimit(1000000)
N = int(sys.stdin.readline())

Narr = []

for i in range(N) :
    Narr.append(list(map(int,sys.stdin.readline().split())))

dp = [ [0]*N for _ in range(N) ]
dp[0][0] = 1

cnt = 0

for i in range(N) :
    for j in range(N) :
        if (i == N-1) and (j == N-1) :
            print(dp[i][j])

        if i + Narr[i][j] < N :
            dp[i + Narr[i][j]][j] += dp[i][j]
        if j + Narr[i][j] < N :
            dp[i][j+ Narr[i][j]] += dp[i][j]

 

이를 정리하면, 
다음 dp에는, 해당 위치 (ex(받은arr의[i][j])에, 전에 다른 위치에서 몇번 왔는지를 정리할 수 있다.

 

즉, 이동하는 값은 -> narr에서

dp에는 -> 그 점까지 왔던 방법을 적는다. 

 

백준 1309

 

import sys

N = int(sys.stdin.readline())

Narr = list(map(int,sys.stdin.readline().split()))

dp = [1] * N

for i in range(N) :
    for j in range(0,i) :
        if Narr[i]>Narr[j] :
            dp[i]= max(dp[i],dp[j]+1)

       

print(max(dp))

 

dp를 선별하는 tip

1. 시간복잡도가 1000000 이 넘는가? - >dfs는 2의 n승으로 감에 주의 (주로 최대 최소)

2. 그렇다면, 재귀식이 나오는가?

3. 이 재귀식은, dp array내부적인 재귀일수도 있지만 손으로 풀어보니 답이 재귀의 형태일 가능성도 있다. 

 

이 기준으로, dp문제임은 쉽게 알아냈지만, 재귀식을 찾는것이 어려웠다. 

간단하게, j까지 자기보다 작은것이 몇개인지를 처음부터 적어가면 되는 구조였다!

 

1->5->3->7시, 1부터(j)n까지(i)로 찾아가게되면, 1,5,7 1,3,7과 같이, 자기보다 작은 애들중 순서가 맞는 애들이 자동으로 나오게 된다.

 

그렇다면, 재귀식은 어떻게 구할것인가

1. 1부터 직접 진행한다.

2. 일정 수 (보통 3~4)까지 진행하고, 재귀식이 나오나 본다.

3. 나오지 않지만 논리상 관계가 있어보인다면, 이를 고민한다(이 과정이 어려운 문제이다)

 

import sys

N = int(sys.stdin.readline())
dp = [1] * (N+1)


if N != 1:
    dp[2] = 2
    for i in range(3,N+1):
        dp[i] = dp[i-1]+dp[i-2]


M = int(sys.stdin.readline())

doit = True
if M == 0 :
    print(dp[N])
    doit = False

answer = 1
lastnumber = 0
for i in range(M) :
        #i는 안씁니다
       
        inputnumber = int(sys.stdin.readline())
        plusnumber = inputnumber-lastnumber-1
        answer = answer * dp[plusnumber]
        lastnumber = inputnumber

       
        if i == (M-1):
            answer = answer*dp[N-inputnumber]
if doit :
    print(answer)
       

 

문제를 풀다보니, 80퍼 이상에서 문제가 발생했다 (indexerror)

이럴땐 반례를 생각하자는 경험이 있어 반례를 보았다. 

 

1. N이 1일때

2. m이 0일때

 

해결하고 나니, 쉽게 풀 수 있었다.

'코딩테스트' 카테고리의 다른 글

BFS/DFS 다시..  (0) 2024.03.06
TREE 관련 문제  (1) 2024.03.03
큐 (심화편)  (1) 2024.02.26
분할 정복 (쿼드 트리)  (0) 2024.02.25
그리디 알고리즘  (0) 2024.02.21