코딩 문제

백준 11053 - 세상에서 제일 긴 증가하는 부분 수열

토리쟁이 2023. 1. 16. 20:53

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

 

 

이 문제는 풀긴 했는데 시간초과가 나서 제대로 푼게 맞는지 모르겠다.

 

시간초과 코드)

n = int(input())
nums = list(map(int, input().split()))
dp = [[1]] * n
d = [1] * n
s = 0
flag = 0
for i in range(n-2, -1, -1):
    for j in range(i+1, n):
        if nums[i] < nums[j]:
            d[i] = d[j]+1
            dp[i].append(d[i])
        d[i] = max(dp[i])

print(max(dp[0]))

뒤에서부터 맨 앞까지 반복문을 돌고 해당 위치의 앞에서부터 끝까지 이중 반복문을 통해 더 큰 것이 있으며 1씩 증가시키고 리스트에 추가하도록 구현했는데, dp를 찍어보니까 내가 원하는 것처럼 들어가지는 않았지만, 값이 잘 나오긴 한다.  나중에 복습할 때 dp에 들어간 원소들이 왜 그렇게 들어가는지 확인해 볼 필요가 있다.

 

풀이 코드는 다른 분의 포스팅을 참고했다.

https://bitwise.tistory.com/215

 

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

https://www.acmicpc.net/problem/11053 11053번: 가장 긴 증가하는 부분 수열 수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인

bitwise.tistory.com

여기에 그림으로 잘 설명되어 있으니 이해가 안가면 다시 보도록 하자.

 

 

풀이 코드)

n = int(input())
nums = list(map(int, input().split()))
dp = [1] * n

for i in range(n):
    for j in range(i):
        if nums[i] > nums[j]:
            dp[i] = max(dp[i], dp[j]+1)

print(max(dp))

생각보다 매우 간단하다.

위 풀이 코드를 리뷰해보자

 

n을 입력받아 길이가 n만큼의 리스트를 생성하였다.

이 리스트 dp는 모든 원소가 1로 초기화되어 있으며, 각 원소는 해당 위치까지에 올 수 있는 가장 긴 증가하는 부분 수열의 길이이다.

각 위치에 숫자가 하나씩 있기 때문에 길이를 1로하여 초기화한 것이다.

 

즉, 다음과 같이 말할 수 있다.

dp[0] : 인덱스 0까지의 증가 부분 수열의 최대 길이

dp[1] : 인덱스 1까지의 증가 부분 수열의 최대 길이

dp[2] : 인덱스 2까지의 증가 부분 수열의 최대 길이

dp[3] : 인덱스 3까지의 증가 부분 수열의 최대 길이

...

dp[0] = 1로 초기화된 상태에서 dp[1]을 구해보자

dp[1]은 인덱스 1까지의 증가 부분 수열의 최대 길이이다.

1번째 숫자가 0번째 숫자보다 크다면 => 즉, 0번째 숫자가 1번째 숫자보다 작다면 길이가 1이 늘어난 2가 dp[1]에 저장될 것이다. 아니라면, 그냥 원래 값이 1을 그대로 가지고 있어야 한다.

 

예제처럼 10 20 10 30 20 50에서 dp[3]을 구해보자

dp[0] = 1

dp[1] = 2

dp[2] = 1

dp[3]을 구하기 위해서는 0~2 인덱스의 수들이 3번째 수보다 작은지 확인하여야 한다.

보면 0, 1, 2 번째 수가 모두 3번째 수인 30보다 작으므로 다 부분 증가 수열이 될 수 있다.

그 중에서도, 가장 긴 길이를 저장해야되므로 dp[0], dp[1], dp[2] 중에서 가장 긴 길이를 갖는 dp값에 자기 자신을 포함하여야 하므로 길이 1을 더하여 저장하면 된다.

dp[1] = 2이 가장 긴 증가 부분 수열의 길이를 갖고 있으므로 2에 1을 더한 3이 dp[3]이 된다.

 

이처럼, 키포인트는 i번째까지의 증가 부분 수열의 최대 길이를 구하기 위해서는 이중 반복문을 사용하여 0번째 수부터 i-1번째 수까지 탐색 후 가장 긴 길이만 가져와 +1해주면 된다는 것이다.

코드로 나타내면 다음과 같다.

for i in range(n):
    for j in range(i):
        if nums[i] > nums[j]:
            dp[i] = max(dp[i], dp[j]+1)

dp[i]는 j가 반복문을 돌아가는 동안 i번째인 수가 더 크다는 조건을 만족할 경우 기존의 dp[i]와 dp[j]+1을 비교하여 더 큰 값으로 계속 업데이트 해줌으로써 모든 수를 탐색하여 max값만 가져올 수 있어진다.

 

이중 반복문을 사용하여 max값을 계속 업데이트 해주며 dp[i]에 저장하는 부분이 평소에 잘 구현하지 않았던 코딩 스타일이라 신기하면서도 깔끔해서 좋았다. 앞으로 나도 자주 써먹어야즹

if nums[i] > nums[j]:
    dp[i] = max(dp[i], dp[j]+1)

'코딩 문제' 카테고리의 다른 글

백준 2565 - 전깃줄  (0) 2023.01.18
백준 11504 - 가장 긴 바이토닉 부분 수열  (0) 2023.01.17
백준 10844 - 쉬운 계단 수  (0) 2023.01.15
백준 2156 - 포도주 시식  (0) 2023.01.14
백준 1463 - 1로 만들기  (0) 2023.01.14