Don't give up!

[백준] 12015번 : 가장 긴 증가하는 부분 수열 2 (java) 본문

Coding Test/BOJ

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

Heang Lee 2021. 8. 21. 20:28

12015번: 가장 긴 증가하는 부분 수열 2 (acmicpc.net)

 

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

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

www.acmicpc.net

어떻게 생각하고 문제를 풀었는가?

주어진 수열에서 부분 수열의 원소들이 인덱스가 증가함에 따라 값도 증가함을 만족한다면 증가하는 부분 수열이 될 수 있습니다.

첫번째 인덱스부터 탐색을 시작하여 부분 수열을 형성하고, 마지막 원소보다 작은 값이 발견되었을 경우 부분 수열을 새로 형성하거나, 해당 원소를 무시하고 다음 원소를 탐색하여 부분 수열에 추가함으로써 가장 긴 증가하는 부분 수열을 찾을 수 있습니다.

결과적으로 반환해야 하는 것은 부분 수열의 최대 길이이므로 부분 수열의 원소를 작은 값으로 갱신함으로써 길이만을 보존하고 답을 찾을 수 있을 것이라고 생각하였습니다.

코드

import java.io.*;
import java.util.ArrayList;
import java.util.StringTokenizer;

class Main {
    private static final ArrayList<Integer> sequence = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        readInput();
        System.out.println(sequence.size()-1);
    }

    private static void readInput() throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine());
        StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
        sequence.add(0);
        for(int i=0; i<n; i++){
            int num = Integer.parseInt(tokenizer.nextToken());
            if(num > sequence.get(sequence.size()-1)){
                sequence.add(num);
            }else{
                int index = lowerBound(num);
                sequence.set(index, num);
            }
        }
        reader.close();
    }

    private static int lowerBound(int target){
        int left = 1;
        int right = sequence.size()-1;
        int result = 0;
        while(left < right){
            int mid = (left+right)/2;
            if(sequence.get(mid) >= target){
                result = mid;
                right = mid-1;
            }else{
                left = mid+1;
            }
        }
        return result;
    }

}

이분 탐색을 사용하여 부분 수열의 최대 길이를 찾는 코드를 작성하였습니다.

O(n)의 탐색으로는 최대 1,000,000 크기의 수열로부터 부분 수열을 찾을 수 없기 때문에 이분탐색을 이용하여 O(nlogn)으로 탐색 시간을 줄일 수 있습니다.

각 인덱스를 탐색하여 부분 수열에 원소를 추가하고, 추가하려는 원소가 부분 수열의 마지막 값보다 작은 값을 갖는 경우 해당 값보다 작거나 같은 가장 큰 원소를 이분 탐색으로 찾고, 해당 원소와 바꾸도록 함으로써 새로운 부분 수열을 형성합니다.

이후 마지막 원소보다 큰 값을 갖는 원소가 추가될 경우 부분 수열에 추가됨으로써 기존 부분 수열의 최대 길이를 찾을 수 있습니다.