Programming/Algorithm

[Java] 프로그래머스 입국심사

Yejii 2021. 3. 5. 01:28

 

문제

입국심사를 기다리는 사람들과 심사를 맡은 심사관들이 있다. 

심사를 기다리는 사람 수를 n, 각각의 심사관이 1명을 심사하는 데 걸리는 시간이 담긴 배열 times가 주어졋을 때

n명 모두 심사를 받기 위해서 걸리는 최소 시간을 리턴하는 문제이다.

 

- 1 <= n <= 10억(명)

- 1분 <= times 요소 <= 10억(분)

- 1 <= times 배열 길이 <= 10만(명)

 

1) 브루트 포스 => 정확도 33.3% 시간초과

처음엔 단순하게 주어진 논리대로 구현했다.

- while문을 돌면서 시간을 1씩 증가시킨다.

- 'int time(심사하는 데 걸리는 시간)', 'int inspected(현재 심사중인 사람 소요시간)' 속성을 담는 Examiner(검사관)클래스를 두었다.

- ArrayList<Examiner> Immigration 을 생성하고, 시간이 1 지날 때마다 inspected를 1씩 증가한다.

- 만약 inspected가 time과 같아지면 complete(완료된 사람)을 하나 증가시키고 해당 인스턴스의 inspected변수를 0으로 초기화한다.

- n에서 완료된 사람을 뺄셈연산하고 n이 0이되는 시간을 리턴한다.

 

하지만 10억명의 사람을 검사하고, 심사관이 10만명으로 주어진 경우에

시간이 1 증가할 때마다 ArrayList를 10만번씩 선형탐색하고, 10억명의 complete가 나올 때까지 반복해야 하기 때문에 효율적이지 못하다.

 

 

2) 이진탐색 => 정확도 100%

문제의 요구사항은 단순히 '최소시간'을 리턴하는 것이다.

따라서 탐색 대상을 '모든 사람이 검사 받는 시간'으로 두고 이진탐색으로 시간을 추정해나갈 수 있다.

- 최소 검사시간 : 1

- 최대 검사시간 : 검사받는 사람 수 * 제일 적은 검사관의 시간

- 시간을 추정해서 해당 시간동안 n명을 검사할 수 있는지를 체크하면 된다. 

- (ex) n = 6, 20 min ? times = {7,10} : 20 / 7 + 20 / 10 = 2 + 2 => 4명 검사 가능 (조건만족x)

- (ex) n = 6 30 min ? times = {7,10} : 30 / 7 + 30 / 10 = 4 + 3 => 7명 검사 가능 (조건만족o)

 

 

코드

import java.util.ArrayList;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        int n = 6;
        int[] times = {7, 10};
//        System.out.println(solution1(n, times)); // 1) 브루트포스 : 시간초과
        System.out.println(solution2(n, times)); // 2) 이진탐색 : 성공
    }


// solution1 
    static class Examiner {
        int time;
        int inspected;

        public Examiner(int time) {
            this.time = time;
            this.inspected = 0;
        }
    }

    static int setInspectedTime(ArrayList<Examiner> immigration) {
        int complete = 0;

        for (int i = 0; i < immigration.size(); i++) {
            Examiner e = immigration.get(i);
            e.inspected++;
            if (e.time == e.inspected) {
                complete++;
                e.inspected = 0;
            }
        }

        return complete;
    }

    static long solution1(int n, int[] times) {
        long current_time = 0;

        Arrays.sort(times);
        ArrayList<Examiner> immigration = new ArrayList<>();
        for (int i = 0; i < times.length; i++) {
            immigration.add(new Examiner(times[i]));
        }

        int wait = n;
        while (wait > 0) { // 등호?
            int complete = setInspectedTime(immigration);
            wait -= complete;
            current_time++;
        }

        return current_time;
    } // solution1 end

    // 2) 이진탐색
    static long solution2(int n, int[] times) {
        Arrays.sort(times);
        long shortest = 1, longest = (long) n * times[0]; 

        long result = Long.MAX_VALUE;
        while (shortest <= longest) {
            long mid = (shortest + longest) / 2;
            if (isPossible(mid, times, n)) {
                result = Math.min(mid, result);
                longest = mid - 1;
            } else {
                shortest = mid + 1;
            }
        }

        return result;
    }

    // 추정한 시간(time) 내에 사람 n명을 검사할 수 있는지 확인
    static boolean isPossible(long time, int[] times, int n) {
        long cnt = 0;
        for (int i = 0; i < times.length; i++) {
            cnt += time / times[i];
        }
        return cnt >= n;
    }
}

 

 

채점결과