
수열이 주어질 때, 그 안에서 증가하는 부분 수열 중 가장 긴 수열의 길이와 해당 수열 자체를 구하는 문제다.
즉, 단순히 LIS의 길이뿐만 아니라, 그 수열의 실제 원소까지 출력해야 한다.
출력 형식은
LIS의 길이
LIS에 포함된 수들
순서로 요구된다.
기본적인 LIS(가장 긴 증가하는 부분 수열)는 DP로 길이를 구할 수 있지만,
이번 문제는 실제로 어떤 원소들로 만들었는지를 역추적해야 한다.
따라서 다음의 2가지 정보를 동시에 저장한다:
dp[i] prev[i] arr[i]가 arr[j]보다 크다면 (j < i)
dp[i] = dp[j] + 1 로 갱신하고 prev[i] = j 로 기록해둔다. 이후, 가장 긴 수열이 끝나는 인덱스에서 prev 배열을 따라가며 역방향으로 수열을 복원한다.
dp[i]: arr[i]를 마지막 원소로 가지는 가장 긴 증가하는 부분 수열(LIS)의 길이 prev[i]: arr[i] 전에 연결된 LIS 원소의 인덱스 (없을 경우 -1)dp[i] = 1, prev[i] = -1이전 원소 중 자신보다 작은 값(arr[j] < arr[i])을 찾아,
그 중 가장 긴 수열에 나를 이어 붙인다.
if (arr[i] > arr[j] && dp[i] < dp[j] + 1)
→ dp[i] = dp[j] + 1
→ prev[i] = j
dp 배열에서 가장 큰 값을 가진 인덱스를 찾아 거기서부터 prev를 역추적해 수열을 구한다.
길이는 max(dp[i]),
수열은 prev 배열을 거슬러 올라가며 복원한다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
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());
StringTokenizer st = new StringTokenizer(br.readLine());
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
int[] dp = new int[n];
int[] prev = new int[n]; // 이전 원소 인덱스 저장용
Arrays.fill(dp, 1);
Arrays.fill(prev, -1);
int lastIdx = 0; // LIS 마지막 인덱스 저장
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (arr[i] > arr[j] && dp[i] < dp[j] + 1) {
dp[i] = dp[j] + 1;
prev[i] = j; // 이전 인덱스 기록
}
}
if (dp[i] > dp[lastIdx]) {
lastIdx = i;
}
}
// LIS 복원
List<Integer> lis = new ArrayList<>();
for (int i = lastIdx; i != -1; i = prev[i]) {
lis.add(arr[i]);
}
Collections.reverse(lis);
// 출력
System.out.println(dp[lastIdx]);
for (int num : lis) {
System.out.print(num + " ");
}
}
}