Algorithm
백준 12015번: 가장 긴 증가하는 부분 수열 (LIS)
10Biliion
2025. 7. 3. 17:09
가장 긴 증가하는 부분 수열 (LIS) 문제는 수열에서 오름차순으로 정렬된 가장 긴 부분 수열의 길이를 구하는 대표적인 알고리즘 문제입니다.
입력: 10 20 10 30 20 50
출력: 4 (예: 10 → 20 → 30 → 50)
이 문제를 해결하는 방법에는 O(N²)의 DP 방식과 O(N log N)의 이분 탐색 방식이 있는데,
입력 수가 많아질수록 O(N log N) 방식이 훨씬 효율적입니다.
이 문제의 입력조건은
' 첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. '
그래서 O(N²) 방식으로 풀 경우 시간초과 입니다. 그래서 O(N log N) 방식으로 LIS의 길이를 구해야 합니다.
전체 코드 (Java)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] arr = new int[n];
StringTokenizer st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
int idx = binarySearch(list, arr[i]);
if (idx < list.size()) {
list.set(idx, arr[i]);
} else {
list.add(arr[i]);
}
}
System.out.println(list.size());
}
public static int binarySearch(List<Integer> list, int target) {
int left = 0;
int right = list.size();
while (left < right) {
int mid = (left + right) / 2;
if (list.get(mid) >= target) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
LIS(가장 긴 증가하는 부분수열)를 리스트에 저장하지 않는다
이 코드에서 list는 실제 증가하는 부분 수열을 저장하는 목적이 아닙니다.
그저 "길이 유지에 필요한 최소한의 값"만을 담는 리스트입니다.
⚠ 예시
arr = [10, 20, 7, 30, 20, 50]
< list 변화 >
10 → [10]
20 → [10, 20]
7 → list.set(0, 7) → [7, 20]
30 → [7, 20, 30]
20 → list.set(1, 20) → [7, 20, 30]
50 → [7, 20, 30, 50]
그런데 실제 LIS 수열은 10 → 20 → 30 → 50일 수도 있음
list는 7로 시작해서 전혀 다른 경로임
시간 복잡도
작업 | 시간 복잡도 |
전체 반복 (N번) | O(N) |
이분 탐색 (각 원소마다) | O(log N) |
전체 | O(N log N) |
입력이 100,000 이상이어도 충분히 통과 가능한 수준