문제풀이/백준

백준 11053 [가장 긴 증가하는 부분 수열] 문제풀이

soo-dal 2024. 1. 10. 09:57

문제 

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

 

11053번: 가장 긴 증가하는 부분 수열

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이

www.acmicpc.net

 

 

 

문제 접근 방식

풀이 접근

가장 긴 증가하는 부분 수열을 찾기 위해서는 특정 항을 기준으로 생각했을 때, 자신보다 작은 항의 개수를 찾아야한다. 

완전탐색으로도 일일히 만들어서 풀 수도 있으나 경우의 수가 너무 많아서 시간 초과가 발생할 것 같다. 

 

완전 탐색으로 접근하기 보다는 어쨋든 이전 항들과의 연관성이 있는 것을 알 수 있기에 DP로 문제를 풀 수 있을 것 같다. 

이제 점화식을 구해보자

 

점화식

점화식을 어떻게 정의해야할까?

문제를 풀다보니 약간의 공통점을 발견할 수 있었는데, 점화식을 정의할 때 'i 번째 까지의 최댓값' 이런 식의 정의 보다는 'i 번째를 포함하여 만들 수 있는 i 번째까지의 최댓값' 이런식으로 정의를 해야하는 것 같다(개인적인 의견). 'i 번째를 포함해서' 라는 정의를 포함할 때 이전 항과 연관지어 생각할 수 있다.

 

이 문제의 점화식을 아래와 같이 정의 했다.

d[ i ] = i 번째를 포함해서 만들 수 있는 부분 수열 길이의 최댓값

 

d[ i ] = i 번째를 포함해서 만들 수 있는 부분 수열 길이의 최댓값

 

d[ i ]를 구하기 위해서는 현재 항의 숫자보다 작은 항을 탐색해서, 그 중 부분 수열의 길이가 최댓값인 것에 +1 을 해주면 i 번째를 포함한 가장 긴 부분 수열의 길이를 구할 수 있다.

 

d[ i ]= max ( d [ j ] +1 if nums[ j ] < nums[i] ) ( j < i )

 

 

이렇게 모든 i 에 대해 d[ i ]를 구하고 이 중 최댓값을 구하면 전체 수열에서 가장 긴 부분 수열의 길이를 구할 수 있다.

 

 

시간 복잡도

for 문으로 돌면서 특정 항에 대한 메모이제이션을 해야한다. 또한, 각 항의 d[ i ]를 구하기 위해서는 이전 항에서 자신 보다 작은 행을 탐색해야하기 때문에 내부에서 for문을 돌려야한다. 그렇기 때문에 시간복잡도는 O(n^2) 이고, n 의 최댓값이 1000이기 때문에 시간내에 동작함을 알 수 있다.

 

 

코드

if __name__=='__main__':
    n=int(input())
    li=list(map(int,input().split()))


    # d[i]=i 번째를 포함해서 만들 수 있는 가장 긴 수열의 길이
    d=[0]*n
    d[0]=1
    for i in range(1,n):
        max_value=1
        for j in range(i):
            if li[j]<li[i]:
                max_value=max(max_value,d[j]+1)
        d[i]=max_value

    print(max(d))

 

 

회고

점화식  d[ i ] 를 정의할 때 단순히 ' i 번째까지의 최댓값' 이런식으로 정의를 하기 보다는, 

'nums[ i ] 를 활용한 i 번째의 최댓값' 이런식으로 한다고 알고 있으면 좀 더 쉽게 점화식을 정의할 수 있을 것 같다.