[PS] BOJ 11053, 11722 / 가장 긴 증가하는 부분 수열

[PS] BOJ 11053, 11722 / 가장 긴 증가하는 부분 수열
문제 링크:
가장 긴 증가하는 부분 수열: https://www.acmicpc.net/problem/11053
가장 긴 감소하는 부분 수열: https://www.acmicpc.net/problem/11722
Thumbnail: Photo by Denys Sudilkovsky (Unsplash)

LIS 유형을 공부하면서, 시리즈를 좀 풀어보려고 합니다. 가장 쉬운 두 문제를 풀어봤습니다.

풀이

LIS 이해하기

LIS(Longest Increase Subsequence, 가장 긴 증가하는 부분 수열)은 전체 수열의 부분 수열 중에서, "증가하는" 부분 수열 중 가장 길이가 긴 것을 찾는 유형입니다. DP와 이분탐색으로 풀 수 있습니다. 이 문제는 DP를 활용해서 풀었습니다.

DP로 풀기

먼저, 원본 수열을 다음과 같이 배열로 나타냅니다.

i 1 2 3 4 5 6
arr[i] 10 20 10 30 20 50

이제, arr[i]를 끝 원소로 가지는 부분 수열의 최대 길이를 dp 배열에 저장해보겠습니다.

i 1 2 3 4 5 6
arr[i] 10 20 10 30 20 50
dp[i] 1 2 1 3 2 4
dp = [1 for _ in range(N)]
for i in range(N):
    for j in range(i + 1, N):
        if A[j] > A[i]:
            dp[j] = max(dp[j], dp[i] + 1)

dp배열 채우기

\(O(N^2)\) 의 시간 복잡도로 LIS를 구할 수 있습니다. 다만, 시간 복잡도로 인해 입력 받는 수열의 길이가 크다면 시간 초과가 발생할 수 있으니, 문제 조건을 미리 확인해 DP로 풀 수 있는지 확인해야 합니다.

가장 긴 감소하는 부분 수열

앞서 본 내용은 '가장 긴 증가하는 부분 수열'에 대한 DP 풀이이며, 이는 BOJ 11053의 풀이이기도 합니다.

동일한 풀이를 사용할 수 있는 다른 문제인 '가장 긴 감소하는 부분 수열' (BOJ 11722)에 대한 풀이도 간단히 정리하겠습니다.

앞서 dp배열을 계산할 때, '가장 긴 증가하는' 이라는 조건을 위해 배열의 두 원소를 비교할 때 다음 원소(j)가 기준값(i)보다 큰지 비교했다면, '가장 긴 감소하는' 이라는 조건을 위해서는 반대로 j가 i보다 작은지 비교하면 됩니다.

dp = [1 for _ in range(N)]
for i in range(N):
    for j in range(i + 1, N):
        if A[j] < A[i]:
            dp[j] = max(dp[j], dp[i] + 1)

dp배열 채우기

전체 코드

BOJ 11053

input = open(0).readline
N = int(input().strip())
A = list(map(int, input().strip().split()))

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

print(max(dp))

solution.py

BOJ 11722

input = open(0).readline
N = int(input().strip())
A = list(map(int, input().strip().split()))

dp = [1 for _ in range(N)]
for i in range(N):
    for j in range(i + 1, N):
        if A[j] < A[i]:
            dp[j] = max(dp[j], dp[i] + 1)

print(max(dp))

solution.py