문제 풀이/백준

[백준] 1965번. 상자 넣기(JAVA)

27200 2025. 1. 10. 13:00

문제

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


풀이(20분)

import java.io.*;
import java.util.*;

public class Main {
    static ArrayList<Integer> list = new ArrayList<>();

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

        int n = Integer.parseInt(br.readLine());
        int[] arr = new int[n];

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

        list.add(arr[0]);
        for(int i = 1; i < n; i++){
            if(list.get(list.size() - 1) == arr[i]){
                continue;
            }

            if(list.get(list.size() - 1) > arr[i]){
                int idx = binarySearch(arr[i]); // 대체할 자리 찾기
                list.set(idx, arr[i]); // 값 대체
                continue;
            }

            list.add(arr[i]); // 가장 큰 값 들어올 경우 리스트 끝에 추가
        }

        System.out.println(list.size());

    }

    public static int binarySearch(int target){
        int start = 0;
        int end = list.size() - 1;
        int mid;

        while(start <= end){
            mid = (start + end) / 2;
            if(list.get(mid) == target){ // 찾으면 바로 반환
                return mid;
            }

            if(list.get(mid) < target){ // 목표치보다 작을 경우 start 를 변경
                start = mid + 1;
                continue;
            }

            end = mid - 1; // 목표치보다 클 경우 end 를 변경
        }

        return start;
    }
}

// 1 6 이 있었고 타겟이 2로 들어옴. 그럼 내가 원하는건 1 2 로 바뀌어야함.
// 그럼 start = 0, end = 1, mid = 0으로 시작함.
// 안 되니까 start = 1로 되고 while문 한번 더.
// 그럼 mid = 1 이 되고 값이 6이 뜨니까 end = 0이 됨.
// 여기서 끝나고 start = 1 이 반환됨.
// 즉 원하는 인덱스 변경 완료

// 왼쪽보다 크면 빼고 넣는다.
// 1
// 1 6
// 1 2
// 1 2 5
// 1 2 5 7
// 1 2 3 7
// 1 2 3 7
// 1 2 3 5
// 1 2 3 5 6
// 즉, 작으면 자기보다 크거나 같은 것 중 최소를 자기 자신으로 대체해야함.

 

가장 긴 증가하는 부분 순열을 찾는 LIS문제와 동일한 방법으로 해결했다.

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

 

1 6 2 5 7 3 5 6

 

라는 예제를 생각해 보자.

 

1을 먼저 넣는다.

 

1은 6에 들어갈 수 있으므로 1 6이 된다.

 

6이 최대 사이즈고 2에 들어갈 수 없으므로, 1을 2에 넣었다고 생각하자. 즉 1, 2가 된다.(박스를 가장 많이 겹치려면 최대인 박스가 가장 작을수록 유리하다.)

 

2는 5에 들어갈 수 있으므로 1 2 5가 된다.

 

5는 7에 들어갈 수 있으므로 1 2 5 7이 된다.

 

7은 3에 들어갈 수 없으므로 1 2 3 7이 된다.

-> 여기서 이해가 힘들 수 있다. 어떻게 1 2 3 7이 되는 것일까???

-> 현재 1 2 5 7까지의 정답은 4이다. 이걸 부정할 생각은 없다.

 

-> 기억을 초기화하고 1 6 2 5 7 3 5까지 있는 상황을 생각해 보자.

-> 그럼 어떻게 넣는 게 가장 많이 포갤 수 있는 것일까? -> 1 2 3 5도 된다는 것이다. 즉, 우리가 3까지만 생각했을 때 가졌던 1 2 3 7보다 1 2 3 5가 되는 것이 뒤에 6이 나올 때 값을 하나 늘릴 수 있는 효율적인 방법이 된다는 소리이다.

 

-> 그건 알겠는데 그럼 1 6 2 5 7 3 6이 나온다면??

-> 결과는 동일하다. 내가 하겠다는 것은 만약 최댓값보다 작은 값이 나온다면, 이 전의 리스트 중에 자기보다 크거나 같은 것 중에 최소인 것을 자기 자신으로 대체하겠다는 것이다.

-> 즉 1 2 5 7 -> 1 2 3 7 -> 1 2 3 6이 된다는 것. 다시 말하지만, 이렇게 해야 뒤에 7이 나오더라도 다시 1 2 3 6 7로 해서 값을 최대로 만들어줄 수 있기 때문이다.

 

만약 이해가 잘 되지 않는다면 11053번 문제를 꼭 다시 풀어보자!!