Coding Test/BaekJoon

[백준] 12015번: 가장 긴 증가하는 부분 수열 2 - java

찬 주 2023. 8. 16. 10:50

문제

https://www.acmicpc.net/problem/12015

 

12015번: 가장 긴 증가하는 부분 수열 2

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000)

www.acmicpc.net

 

https://st-lab.tistory.com/285 해당 게시물을 참고하였습니다. 

 

[백준] 12015번 : 가장 긴 증가하는 부분 수열 2 - JAVA [자바]

https://www.acmicpc.net/problem/12015 12015번: 가장 긴 증가하는 부분 수열 2 첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000,000) w

st-lab.tistory.com

 

접근법

LIS 문제이다. LIS가 뭔지 모른다면 이 문제를 먼저 풀고 오는 것을 추천한다. 여기서는 2중 for문을 사용해 문제를 풀었지만, 이번 문제는 N이 최대 1,000,000이므로 다른 알고리즘을 생각해야 한다. 

 

LIS를 가지고 있는 리스트를 만들어 구현해보자. 수열 A를 탐색하며 A[i]까지의 최장 길이를 구할 때 2가지 경우가 있다.

1. LIS의 마지막보다 큰 경우

2. LIS의 마지막보다 작은 경우

 

1.의 경우 LIS의 마지막에 A[[i]를 추가해주면 된다.

 

2.의 경우 어떻게 해야될지 생각해보자. A[i]가 들어갈 자리를 찾아야한다. 우리가 현재 구하고자 하는 것은 가장 긴 증가 수열이다. 가장 긴 증가 수열을 만들고 싶다면 인접한 수의 차가 작도록 하면 된다. 그렇다면 LIS 리스트에서 A[i]보다 크거나 같은 수를 A[i]로 대체해주면 되지 않을까? (이 개념이 이해되지 않는다면 이분 탐색의 Lower Bound, Upper Bound를 공부하자.)

 

여기서 주의할 점은 마지막에 LIS를 출력했을 때 우리가 구하고자한 LIS가 아닐 수 있다. 하지만 길이는 구하고자하는 LIS의 길이와 똑같다. 이 이유는 위에서 수를 대체해줬기 때문이다. 여기서 이해가 안 될 수도 있다. 아래의 예시를 같이 풀어보자

A[2]까지 for문을 진행했을 때 결과이다. (위에서 1.의 경우는 쉬우니 그냥 넘어가도록 하겠다.)

A[3] = 15의 위치를 찾아보면 15보다 크거나 같은 수 중 인덱스가 가장 작은 값은 20이므로 20을 15로 대체해준다.

 

A[4] = 20의 위치를 찾아보면 20보다 크거나 같은 수 중 인덱스가 가장 작은 값은 30이므로 30을 20으로 대체해준다. A[5], A[6]은 그저 뒤에 넣어주기만 하면 된다.

A[7] = 40의 위치를 찾아보면 40보다 크거나 같은 수 중 인덱스가 가장 작은 값은 50이므로 50을 40으로 대체해준다. A[8], A[9]는 그저 뒤에 넣어주기만 하면 된다.

중간 중간 대체해주는 과정에서 대체를 안 해주면 그 다음 수들이 LIS 리스트에 들어올 수 없다. 만약에 주어진 수열이 {10, 20, 30, 15, 20, 30, 50, 40} 까지라고 하더라도 {10, 15, 20, 30, 50} 이나 {10, 15, 20, 30, 40} 이나 길이는 같다. 따라서 우리는 '길이'를 구하는데에만 집중하면 된다. 

 

코드

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 {

    static int N;
    static int[] A;
    static int[] dp;
    static List<Integer> LIS = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        N = Integer.parseInt(br.readLine());

        A = new int[N];
        dp = new int[N];

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            A[i] = Integer.parseInt(st.nextToken());
        }

        LIS.add(A[0]);
        for (int i = 1; i < N; i++) {
            if (LIS.get(LIS.size() - 1) < A[i]) {
                LIS.add(A[i]);
            } else {
                int idx = lowerBound(0, LIS.size() - 1, A[i]);
                LIS.set(idx, A[i]);
            }
        }

        System.out.println(LIS.size());
    }

    private static int lowerBound(int L, int R, int value) {
        while (L < R) {
            int mid = (L + R) / 2;

            if (LIS.get(mid) >= value) {
                R = mid;
            } else {
                L = mid + 1;
            }
        }

        return R;
    }
}