알고리즘

[Lv.3] 프로그래머스 - 입국심사 (이분탐색)

뚜벅-뚜벅 2021. 5. 20. 19:28

문제 설명

n명이 입국심사를 위해 줄을 서서 기다리고 있습니다. 각 입국심사대에 있는 심사관마다 심사하는데 걸리는 시간은 다릅니다.

처음에 모든 심사대는 비어있습니다. 한 심사대에서는 동시에 한 명만 심사를 할 수 있습니다. 가장 앞에 서 있는 사람은 비어 있는 심사대로 가서 심사를 받을 수 있습니다. 하지만 더 빨리 끝나는 심사대가 있으면 기다렸다가 그곳으로 가서 심사를 받을 수도 있습니다.

모든 사람이 심사를 받는데 걸리는 시간을 최소로 하고 싶습니다.

입국심사를 기다리는 사람 수 n, 각 심사관이 한 명을 심사하는데 걸리는 시간이 담긴 배열 times가 매개변수로 주어질 때, 모든 사람이 심사를 받는데 걸리는 시간의 최솟값을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 입국심사를 기다리는 사람은 1명 이상 1,000,000,000명 이하입니다.
  • 각 심사관이 한 명을 심사하는데 걸리는 시간은 1분 이상 1,000,000,000분 이하입니다.
  • 심사관은 1명 이상 100,000명 이하입니다.

입출력 예

n        times             return

6 [7, 10] 28

입출력 예 설명

가장 첫 두 사람은 바로 심사를 받으러 갑니다.

7분이 되었을 때, 첫 번째 심사대가 비고 3번째 사람이 심사를 받습니다.

10분이 되었을 때, 두 번째 심사대가 비고 4번째 사람이 심사를 받습니다.

14분이 되었을 때, 첫 번째 심사대가 비고 5번째 사람이 심사를 받습니다.

20분이 되었을 때, 두 번째 심사대가 비지만 6번째 사람이 그곳에서 심사를 받지 않고 1분을 더 기다린 후에 첫 번째 심사대에서 심사를 받으면 28분에 모든 사람의 심사가 끝납니다.


[접근]

머리로는 알겠는데 구현으로 옮기기 정말 어려운 문제였다

최대 심사 시간이 가장 작은 경우를 만드는게 핵심이었는데, 어차피 times 중 한 값의 배수니까... 까지 생각하고

어떤 값을 이분탐색해야할지 몰라 한참 헤맸다. 머리를 싸매다가 결국 다른분의 접근을 참고하였다.

 

1.최소시간, 최대시간을 start, end 로 설정한다.

2. 이를 이분탐색하여 mid 값 마다 각 times 로 나눈 누적합을 구한다

(ex. mid가 28분이면 7분심사대 4명, 10분심사대 2명 => 총 6명 심사 가능)

 

처음코드(오답)

def solution(n, times):
    answer = 0
    # allt = []
    # for t in times:
    #     for i in range(1,n+1):
    #         allt+=[t*i]
    # allt.sort() times의 모든 시간들의 배수 배열 정렬하면 시간초과됨..
    start = 1
    end = max(times)*n
    while start<=end:
        total = 0
        mid = (start+end)//2
        for t in times:
            total+=mid//t
        if total <=n:
            start = mid+1
        if total >n :
            end = mid -1
        answer = start
        
    #최악심사시간을 max로 두고, 최소 심사 min
    return answer

 

위 코드처럼 하면 자꾸 29 가 출력된다.

times 중 하나라도 나눠 떨어지는 값이어야 한다는 생각이 들어

조건을 이리저리 주고, times 의 배수들로 배열을 만들어 보았으나

해당 TC 만 통과하고 채점에서 시간초과를 받았다. 

 

알고보니 이분탐색의 조건이 잘못되었다.

 

def solution(n, times):
    answer = 0
    start = 1
    end = max(times)*n
    while start<end:
        total = 0
        mid = (start+end)//2
        for t in times:
            total+=mid//t
        if total <n:
            start = mid+1
        if total >=n :
            end = mid
        answer = start
        
    return answer

이분탐색 while 문에서 조건에서 등호가 빠지고, mid 가 아닌 start 를 반환하게 바뀌었더니 통과가 됐다.

 

아마도 최소시간을 구하는 문제이기 때문에 나처럼 29 와 같이 7이나 10으로 나눈 몫의 합이

n 과 일치하는 더 큰 수가 생기기 때문 아닐까 싶다. start의 경우 심사할 수 있는 인원보다 적은 경우 계속 mid+1을 해주고, 많거나 같은 경우에는 end에 mid를 대입하므로, 조건을 만족하는 최소값은 start 가 되는 것이다.

 

뭐랄까 아직도 촉으로는 이해했으나 정확하게 설명하기 힘든 문제인 것 같다.

통나무 베기 문제처럼 어떤 값을 이분탐색해야할 지 모를때 되새겨보면 좋을 듯 하다.