PS

[python] 가장 긴 증가하는 부분 수열 시리즈

jungh150c 2024. 5. 21. 21:36

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

 

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

 

import sys
input = sys.stdin.readline

n = int(input())
a = list(map(int, input().split()))

dp = [0 for _ in range(n)]
maxa = 0

for i in range(n):
    maxt = 0
    for j in range(i):
        if a[i] > a[j]:
            maxt = max(maxt, dp[j])
    dp[i] = maxt + 1
    maxa = max(maxa, dp[i])

print(maxa)

dp[i] : 0부터 i까지 i를 포함하는 가장 길게 증가하는 수열의 길이

앞의 계산을 뒤의 계산에서 활용하기 위해서 마지막 요소를 포함시켜 점화식을 정의하는 방식이다. dp에서 자주 보이는 방식이다.

점화식 : dp[i] = max(dp[k]) + 1 (dp[i]보다 dp[k]가 작은 0과 i-1 사이의 k에 대해...)

(정답)

 


 

가장 긴 증가하는 부분 수열 2 (백준 12015번)

 

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

 

import sys
input = sys.stdin.readline

n = int(input())
a = [0] + list(map(int, input().split()))

dp = [0 for _ in range(n + 1)]
b = [0 for _ in range(n + 1)]
idx = 0
maxl = 1
b[1] = a[1]
dp[1] = 1

def binarysearch(l, r, now):
    while l < r:
        mid = (l + r) // 2
        if b[mid] < now:
            l = mid + 1
        else:
            r = mid
    return l

for i in range(2, n + 1):
    if b[maxl] < a[i]:
        maxl += 1
        b[maxl] = a[i]
        dp[i] = maxl
    else:
        idx = binarysearch(1, maxl, a[i])
        b[idx] = a[i]
        dp[i] = idx

print(maxl)

dp 배열과 점화식은 위와 동일하다. 그런데 위의 풀이대로 풀면 시간 초과가 나온다. (오답)

시간을 줄이기 위해 dp 배열을 채울 때 순차 탐색이 아닌 이분 탐색을 활용하여야 한다. 

이분 탐색은 정렬된 데이터에만 적용할 수 있으므로 필요한 데이터들을 추가 공간 b에 오름차순 정렬이 되도록 채워가며 b에서 이분 탐색을 해주는 것이다. (정답)

 


 

가장 긴 증가하는 부분 수열 3 (백준 12738번)

 

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

 

import sys
input = sys.stdin.readline

n = int(input())
a = [0] + list(map(int, input().split()))

dp = [0 for _ in range(n + 1)]
b = [0 for _ in range(n + 1)]
idx = 0
maxl = 1
b[1] = a[1]
dp[1] = 1

def binarysearch(l, r, now):
    while l < r:
        mid = (l + r) // 2
        if b[mid] < now:
            l = mid + 1
        else:
            r = mid
    return l

for i in range(2, n + 1):
    if b[maxl] < a[i]:
        maxl += 1
        b[maxl] = a[i]
        dp[i] = maxl
    else:
        idx = binarysearch(1, maxl, a[i])
        b[idx] = a[i]
        dp[i] = idx

print(maxl)

두번째 문제와 동일하게 풀면 되었다. (정답)

 


 

가장 긴 증가하는 부분 수열 4 (백준 14002번)

 

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

 

import sys
input = sys.stdin.readline

n = int(input())
a = [0] + list(map(int, input().split()))

dp = [0 for _ in range(n + 1)]
maxa = 0

for i in range(1, n + 1):
    maxt = 0
    for j in range(i):
        if a[i] > a[j]:
            maxt = max(maxt, dp[j])
    dp[i] = maxt + 1
    maxa = max(maxa, dp[i])

idx = maxa
ans = [0 for _ in range(idx + 1)]
for i in range(n, 0, -1):
    if dp[i] == idx:
        ans[idx] = a[i]
        idx -= 1

print(maxa)
for i in range(1, maxa + 1):
    print(ans[i], end = ' ')

dp 배열과 점화식은 위와 동일하다. 그런데 이번에는 가장 긴 증가하는 부분 수열의 길이뿐만 아니라 해당 수열까지 출력을 해야 한다.

나머지 풀이(dp 정의, dp 채우기 등)는 동일한데 마지막에 dp 배열을 역순으로 하나씩 탐색하며 저장한 뒤 출력하면 된다. (정답)

 


 

가장 긴 증가하는 부분 수열 5 (백준 14003번)

 

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

 

import sys
input = sys.stdin.readline

n = int(input())
a = [0] + list(map(int, input().split()))

dp = [0 for _ in range(n + 1)]
b = [0 for _ in range(n + 1)]
idx = 0
maxl = 1
b[1] = a[1]
dp[1] = 1

def binarysearch(l, r, now):
    while l < r:
        mid = (l + r) // 2
        if b[mid] < now:
            l = mid + 1
        else:
            r = mid
    return l

for i in range(2, n + 1):
    if b[maxl] < a[i]:
        maxl += 1
        b[maxl] = a[i]
        dp[i] = maxl
    else:
        idx = binarysearch(1, maxl, a[i])
        b[idx] = a[i]
        dp[i] = idx

idx = maxl
ans = [0 for _ in range(idx + 1)]
for i in range(n, 0, -1):
    if dp[i] == idx:
        ans[idx] = a[i]
        idx -= 1

print(maxl)
for i in range(1, maxl + 1):
    print(ans[i], end = ' ')

세번째 문제와 네번째 문제를 합치기만 하면 된다. 

즉, dp 배열을 채울 때는 이분 탐색을 이용해서 채우고, 마지막에 출력할 때는 dp 배열을 거꾸로 탐색하며 정답 수열을 구하는 것이다. (정답)