Algorithm/백준(BOJ)
[백준] 14003. 가장 긴 증가하는 부분 수열 5 (Java)
Carroti
2022. 10. 7. 00:49
문제
14003번: 가장 긴 증가하는 부분 수열 5
첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (-1,000,000,000 ≤ Ai ≤ 1,000,000,000)
www.acmicpc.net
알고리즘
이분 탐색
풀이
가장 긴 증가하는 부분 수열 3 문제와 동일하지만 LIS 수열까지 출력해줘야하는 문제이다.
이분 탐색을 이용한 방식은 LIS의 길이는 보장하지만 LIS 수열은 보장하지 않았다.
LIS 수열을 구하기 위해 수열의 각 원소가 리스트의 어느 위치에 들어갔었는지 기록하고,
수열의 뒤에서부터 역추적하여 해결하였다.
[2 4 1 8 7 5 9 0 3 6] 의 수열로 표를 그리면 다음과 같다.
list의 크기, 즉 lis의 길이가 4이므로
index 배열의 뒤에서부터 3~0까지 값을 가진 첫 숫자들이 LIS 를 이루는 수임을 알 수 있다.
뒤에서부터 탐색하므로 스택을 이용해 꺼내며 출력하였다.
* 앞에서부터 0~3을 탐색하면 올바른 LIS를 찾지 못한다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
static ArrayList<Integer> list;
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
list = new ArrayList<>();
int N = Integer.parseInt(br.readLine());
int[] indexArr = new int[N];
int[] arr = new int[N];
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
for (int i = 0; i < N; i++) {
int num = Integer.parseInt(st.nextToken());
arr[i] = num;
if (i == 0 || list.get(list.size() - 1) < num) {
list.add(num);
indexArr[i] = list.size() - 1;
} else {
int index = lowerBound(num);
list.set(index, num);
indexArr[i] = index;
}
}
Stack<Integer> stack = new Stack<>();
int index = list.size() - 1;
for (int j = N - 1; j >= 0; j--) {
if (indexArr[j] == index) {
stack.push(arr[j]);
index--;
}
}
sb.append(list.size()).append("\n");
while(!stack.isEmpty())
sb.append(stack.pop()).append(" ");
System.out.println(sb);
}
static int lowerBound(int num) {
int left = 0;
int right = list.size();
while (left < right) {
int mid = (left + right) / 2;
if (list.get(mid) < num)
left = mid + 1;
else
right = mid;
}
return right;
}
}