N
: 수열 A의 크기 ()
A
: 입력받은 수열 ()
입력받은 수열에서 가장 긴 증가하는 부분 수열의 길이를 출력하는 문제이다.
N과 A의 크기가 매우 크기 때문에 모든 부분 수열을 구하고 찾는 방식으로 푼다면 시간 초과가 발생할 것이다.
→ 최장 증가 부분 수열(LIS) 알고리즘을 활용한다.
⭐️ LIS (Longest Increasing Subsequence)
- 가장 긴 증가하는 부분 수열
- 구현 방법
- DP 활용 (시간복잡도 : )
- 이분탐색 활용 (시간복잡도 : )
### DP 활용 dp = [1] * n for i in range(n): for j in range(i): # 첫번째 ~ i번째까지 비교 if arr[i] > arr[j]: $ 뒤에 있는 값보다 크다면 dp[i] = max(dp[i], dp[k]+1)
시간복잡도를 줄이기 위해 이분탐색 활용한 방법으로 문제를 푼다.
LIS 이분탐색 구현
left, right = 0, len(answer)
left
가 right
와 같거나 커지는 경우left
값 증가 (더 큰 수로 이동)right
값 감소 (더 작은 수로 이동)LIS 이분탐색 →
최종 시간복잡도
로,최악의 경우 이 되어 제한 시간 1초 내에 연산이 가능하다.
LIS를 이분탐색으로 구현해 탐색
import sys
input = sys.stdin.readline
# N 입력
N = int(input())
# 수열 A 입력
A = list(map(int, input().split()))
# 정답 저장 리스트 정의 (인덱스 계산 편리 위해 초기값 0)
answer = [0]
# 이분 탐색 LIS 방법 적용
for a in A:
# 지금까지 저장된 수열의 마지막 값이 a보다 작다면 증가하는 중 = 추가
if answer[-1] < a:
answer.append(a)
# 아니면 들어갈 위치를 이분탐색으로 확인
else:
left, right = 0, len(answer) # 정답 리스트 내에서 탐색
# 이분탐색 정의
while left < right:
mid = (left + right) // 2
if answer[mid] < a:
left = mid + 1 # a가 크면 왼쪽 이동
else:
right = mid # a가 작으면 오른쪽 이동
# 찾은 위치에 값 추가
answer[right] = a
# 결과 출력
print(len(answer) - 1)