입력: [10, 20, 10, 30, 20, 50]
출력: 3 (가능한 LIS: [10, 20, 30] 또는 [10, 20, 50])
이 문제를 해결하는 대표적인 방법은 다음과 같음
✅ 알고리즘 개요
dp[i] = max(dp[j] + 1) // (단, j < i이고 arr[j] < arr[i]인 경우)

package silver.silver2;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
public class Problem11053 {
public static void main(String[] args) throws IOException {
// 입력
System.setIn(new FileInputStream("src/input.txt"));
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
// LIS
int[] dp = new int[n];
Arrays.fill(dp, 1);
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (arr[j] < arr[i]) {
dp[i] = Math.max(dp[j] + 1, dp[i]);
}
}
}
// 배열의 최대값
System.out.println(Arrays.stream(dp).max().getAsInt());
}
}
✅ 알고리즘 개요
Collections.binarySearch()는 중복된 값이 있을때는 사용을 못하니 lower_bound를 직접 구현하는 방법을 사용하는것이 좋음



package gold.gold2;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Problem12015 {
static List<Integer> lis = new ArrayList<>();
public static void main(String[] args) throws IOException {
// 입력
System.setIn(new FileInputStream("src/input.txt"));
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
for (int i = 0; i < n; i++) {
int insertIndex = lowerBound(arr[i]);
if (insertIndex == lis.size()) { // 리스트의 끝에 추가해야한다면
lis.add(arr[i]);
} else {
lis.set(insertIndex, arr[i]); // 교체
}
}
System.out.println(lis.size());
}
private static int lowerBound(int value) {
int st = 0;
int en = lis.size();
while (st < en) {
int mid = (st + en) / 2;
if (lis.get(mid) >= value) {
en = mid;
} else {
st = mid + 1;
}
}
return st;
}
}
✅ 경로 추적을 활용한 LIS 복원 (O(N log N))

package platinum.platinum5;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;
public class Problem14003 {
static List<Integer> lis = new ArrayList<>();
static int[] idx;
public static void main(String[] args) throws IOException {
// 입력
System.setIn(new FileInputStream("src/input.txt"));
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
idx = new int[n];
for (int i = 0; i < n; i++) {
int insertIndex = lowerBound(arr[i]);
if (insertIndex == lis.size()) { // 리스트의 끝에 추가해야한다면
lis.add(arr[i]);
} else {
lis.set(insertIndex, arr[i]); // 교체
}
idx[i] = insertIndex; // 현재 원소가 LIS의 몇 번째 위치인지 저장
}
int len = lis.size() - 1;
StringBuilder sb = new StringBuilder();
Stack<Integer> s = new Stack<>();
// 뒤에서부터 LIS를 역추적하여 스택에 저장
for (int i = n - 1; i >= 0; i--) {
if (idx[i] == len) { // LIS의 현재 길이(len)에 해당하는 값 찾기
s.push(arr[i]); // LIS 원소 저장
len--; // 다음 원소 찾기 위해 감소
}
}
// 스택에서 꺼내며 LIS를 순서대로 출력
while (!s.isEmpty()) {
sb.append(s.pop()).append(" ");
}
System.out.println(lis.size()); // LIS 길이 출력
System.out.println(sb);
}
private static int lowerBound(int value) {
int st = 0;
int en = lis.size();
while (st < en) {
int mid = (st + en) / 2;
if (lis.get(mid) >= value) {
en = mid;
} else {
st = mid + 1;
}
}
return st;
}
}
✅ LIS를 활용한 최소 변경 횟수 구하기
- 일부 문제에서는 최소한의 변경으로 수열을 정렬해야 할 때, LIS(최장 증가 부분 수열)의 길이를 구한 후
N - LIS 길이를 계산하면 해결되는 경우가 많음- 예시 문제 유형:
- 배열을 정렬하기 위한 최소 변경 횟수
- 최소한의 원소를 제거하여 수열을 정렬하는 문제
- 연속된 숫자의 최장 부분 수열을 유지하며 최소 제거 횟수 구하기
- 백준 2631 - 줄세우기