가장 긴 증가하는 부분 수열 2
가장 긴 증가하는 부분 수열 3
2,3 은 둘다 N (1 ≤ N ≤ 1,000,000) 범위를 가진다.
기존 1에 비해서 범위가 크기에 O(N^2) 알고리즘을 선택하면 시간초과가 나게된다.
(기존 DP 배열을 통한 해결은 불가능)
그렇기 때문에 LIS 와 이분탐색 개념을 섞어서 해결해야 한다.
LIS 문제의 중점은 증가와 가장 긴 경우를 구해야한다.
주요 해결 로직은
1. arr[0] 을 넣고 시작
2. 다음 값부터 arr 배열에서 탐색을 진행한다.
반복.
이 알고리즘은 최대한 간격을 작게해서 최대한 배열을 길게 끌고가는 것이다.
이게 가능한 이유는 LIS 는 결국 오름차순으로 정렬되기에 이분탐색이 적용 가능한 것이다.
'대치'를 하는 이유는 당연하게 삽입을 하게되면 순서가 안맞는 경우가 생긴다.
ex) {10, 20, 30, 15} -> {10, 15, 30}(X) {10, 20, 30}(O)
해당 알고리즘은 LIS 배열에 있는 요소들과 현재 요소를 비교하기만 하므로, 음수와 양수 모두 포함된 배열에서도 자연스럽게 비교가 이루어진다.
즉, 단지 수와 수의 대소비교만 하기때문에 음수이든 양수이든 상관없이 동작한다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
/*
1초 / 512MB
---
가장 긴 증가하는 부분 수열 2
LIS 문제이다.
기존 방법으로 접근하면 O(N^2)로 시간초과가 나게된다.
그렇기 때문에 LIS 와 이분탐색 개념을 섞어서 해결해야 한다.
LIS 문제의 중점은 증가와 가장 긴 경우를 구해야한다.
주요 해결 로직은
1. arr[0] 을 넣고 시작
2. 다음 값부터 arr 배열에서 탐색을 진행한다.
- 마지막 값보다 클 경우 추가한다.
- 마지막에 넣을 수 없다면 넣을 자리를 찾아야 하기에 이분 탐색 진행
- 넣을 자리를 찾았다면 삽입이 아닌 '대치'를 해야함.
반복.
---
N (1 ≤ N ≤ 1,000,000)
*/
public class Main {
public static int[] LIS, arr;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
StringTokenizer st = new StringTokenizer(br.readLine());
LIS = new int[N];
arr = new int[N];
for (int i=0; i<N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
LIS[0] = arr[0]; // 초기 값 설정
int lengthOfLIS = 1;
for (int i=1; i<N; i++) {
int key = arr[i];
// 마지막값보다 크다면 배열 마지막에 추가
if (LIS[lengthOfLIS-1] < key) {
LIS[lengthOfLIS++] = key;
}
else { // 이분탐색을 진행한다.
int low = 0;
int high = lengthOfLIS;
while (low < high) {
int mid = (low + high)/2;
if (LIS[mid] < key) {
low = mid + 1;
} else {
high = mid;
}
}
LIS[low] = key;
}
}
System.out.println(lengthOfLIS);
}
}