처음엔 “수열을 앞에서부터 보면서, 각 위치에서 끝나는 LIS의 길이”를 저장하면 되지 않을까?
라고 생각했다.
이 문제는 “구간”이 아니라 “부분 수열”이기 때문에
어떤 수 seq[i]를 선택하려면,
그 이전의 원소 seq[j] (j < i) 중에서
seq[j] < seq[i]
이 조건을 만족하는 것들만 고려해야 한다.
그래서 DP 테이블을 다음처럼 정의했다.
dp[i] = i번째 원소를 ‘마지막 원소’로 갖는 LIS의 길이
LIS는 Longest Increasing Subsequence의 약자로, 가장 긴 증가하는 부분 수열을 의미한다.
이렇게 정의해두면 자연스럽게 점화식도 나온다.
i번째 위치에서 고려할 선택지는 두 가지뿐이다:
dp[j] + 1 로 이어붙이기1즉,
dp[i] = max(dp[j] + 1) (단, j < i 이고 seq[j] < seq[i])
dp[i] = 1 (만약 조건을 만족하는 j가 없다면)
아래 코드는 위 아이디어대로 구현한 O(N²) 정석 풀이다.
n = int(input())
seq = list(map(int, input().split()))
dp = [0] * n
dp[0] = 1
for i in range(1, n):
candidates = []
for j in range(i):
if seq[i] > seq[j]:
candidates.append(dp[j] + 1)
if candidates:
dp[i] = max(candidates)
else:
dp[i] = 1
print(max(dp))
이 코드의 시간복잡도는
바깥 for → N번
안쪽 for → 평균 N/2 정도
총 ≈ N²/2 → 대략 50만번 정도 (N = 1000일 때)
파이썬에서 50만 연산은 아주 가벼운 수준이라서
백준 기준 1초 안에 충분히 통과한다.
| N 크기 | 안전한 시간복잡도 (Python 기준) |
|---|---|
| N ≤ 1,000 | O(N²) OK |
| N ≈ 5,000 | O(N²)는 경계, O(N log N) 추천 |
| N ≈ 100,000 | O(N log N) 또는 O(N)만 가능 |
| N ≥ 1,000,000 | 사실상 O(N) 이하 |
→ 이 문제는 N ≤ 1000이므로 O(N²) 풀이가 정석.