ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 12764번 싸지방에 간 준하
    Algorithm Study/Python 2021. 5. 29. 16:50

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

     

    12764번: 싸지방에 간 준하

    첫째 줄에 사람의 수를 나타내는 \(N\)이 주어진다. \((1 \le N \le 100,000)\) 둘째 줄부터 \(N\)개의 줄에 걸쳐서 각 사람의 컴퓨터 이용 시작 시각 \(P\)와 종료 시각 \(Q\)가 주어진다. \((0 \le P \lt Q \le 1,000

    www.acmicpc.net

    문제

    현재 대한민국 해군에 소속되어있는 준하는 문제를 풀기 위해 매일같이 사이버 지식 정보방 통칭 싸지방에 다닌다. 그러나 최근 문제가 생겼다. 싸지방에 사람이 몰려 컴퓨터 수가 모자라게 된 것이다. 이런 사태를 도저히 용납할 수 없었던 준하는 곧 전역하는 선임을 설득해 민원을 넣도록 하는 데 성공했다.

    마침내 부대에서는 민원을 받아들이기로 하였고, 컴퓨터를 증설하기로 했다. 또한, 컴퓨터 간의 사용률에 따라 다른 성능의 컴퓨터를 설치하고자 한다.

    하지만 예산이 부족해 사람 수 만큼 컴퓨터를 살 수가 없었다. 고심에 고심을 거듭한 준하는 모든 사람이 항상 정해진 시간에 싸지방을 이용한다는 사실을 발견했다.

    컴퓨터가 있는 자리에는 1번부터 순서대로 번호가 매겨져 있다. 모든 사람은 싸지방에 들어왔을 때 비어있는 자리 중에서 번호가 가장 작은 자리에 앉는 것이 규칙이다.

    준하가 발견한 사실과 이용 규칙을 가지고, 모든 사람이 기다리지 않고 싸지방을 이용할 수 있는 컴퓨터의 최소 개수와 자리별로 몇 명의 사람이 사용했는가를 구하시오.

     

    입력

    첫째 줄에 사람의 수를 나타내는 N이 주어진다. (1≤N≤100,000) 둘째 줄부터 N개의 줄에 걸쳐서 각 사람의 컴퓨터 이용 시작 시각 P와 종료 시각 Q가 주어진다. (0≤P<Q≤1,000,000)

    시작 시각이나 종료 시각이 다른 사람과 겹치는 경우는 없다.

     

    출력

    첫째 줄에 사람이 모든 사람이 기다리지 않아도 되는 컴퓨터의 최소 개수 X를 출력한다.

    둘째 줄에는 1번 자리부터 X번 자리까지 순서대로 각 자리를 사용한 사람의 수를 띄어쓰기 간격으로 출력한다.

     

     

    풀이

    N이 10만이기 때문에 단순탐색으로 자리를 찾는 경우에는 시간 조건을 통과시킬 수 없다.

     

    time_set = [list(map(int, sys.stdin.readline().split()))for _ in range(N)]
    time_set.sort(key=lambda x: (x[START], x[END]))
    time_set = deque(time_set)

    먼저 입력 받은 시작과 끝나는 시간을 정렬하고 앞에서부터 가져올 수 있게 하기위해 deque를 사용했다.

     

    empty = [i for i in range(N)]
    heapq.heapify(empty)            #사용할 수 있는 컴퓨터

    사용하는 컴퓨터의 최대 수는 사람 수이기 때문에 사람 수만큼의 번호를 넣어서 비어있는 컴퓨터를
    나타내는 heap을 구성하였다. 항상 가장 낮은 번호의 비어있는 컴퓨터가 맨 위에 위치한다.

     

    while time_set:
        tick += 1
        if tick == time_set[0][START]:
        	# temp의 START는 시작시간 END는 종료시간
            temp = time_set.popleft()
            idx = heapq.heappop(empty)
            # 종료시간인 temp[END] 기준으로 min heap을 구성하기 위해 반대로 입력
            heapq.heappush(computers, [temp[END], temp[START], idx])
            count[idx] += 1

    이제 time_set이 빌때까지 tick을 1씩 증가시키면서 time_set의 0번에 있는 시작 시간이 tick과 동일한 경우에
    꺼낸 뒤 heap에 넣었다. heap에는 종료 시간이 먼저들어가게 하여 종료 시간을 기준으로 min heap을 생성하여
    가장 빨리 종료하는 사람이 heap에 맨 위에 오게 설정하였다.

    time_set의 0번을 확인하는 이유는 정렬된 time_set에서 앞에서부터 데이터를 pop하기 때문에 남아 있는 사람 중
    가장 먼저 컴퓨터를 사용하는 사람이 항상 0번에 있다.

     

    idx = heapq.heappop(empty)
    count[idx] += 1

    idx는 어떤 자리에 앉았는지 체크하기 위한 변수로 empty를 pop하면 가장 낮은 번호의 비어있는 컴퓨터가 나오기 때문에 가장 앞에 있는 컴퓨터에 앉을 수 있다.
    count는 컴퓨터 idx에 앉은 횟수를 저장한다.

     

        if computers and computers[0][0] == tick:
            s, e, i = heapq.heappop(computers)
            heapq.heappush(empty, i)

    heap에 가장 위에 있는 값이 현재 tick과 동일한 경우에는 pop하고 몇 번자리에 앉았는지 체크해서
    empty heap에 다시 넣어 그 자리를 사용할 수 있게 변경하였다.

     

    answer = [0]            # 0번은 갯수 1번부터 사용한 횟수
    for i in count:
        if i == 0:
            break
        else:
            answer.append(i)
            answer[0] += 1
            
    
    print(answer[0])
    print(*answer[1:])
    

    answer에 0번에는 총 필요한 컴퓨터의 수 1번부터는 사용횟수가 0이 아닌 컴퓨터를 순서대로 append했다.
    예제에 대한 answer = [4, 1, 2, 1, 1]

     

     

     

    전체 코드

    import sys
    import heapq
    from collections import deque
    
    START, END = 0, 1
    N = int(sys.stdin.readline().strip())
    time_set = [list(map(int, sys.stdin.readline().split()))for _ in range(N)]
    time_set.sort(key=lambda x: (x[START], x[END]))
    time_set = deque(time_set)
    computers = []      #heap
    empty = [i for i in range(N)]
    heapq.heapify(empty)            #사용할 수 있는 컴퓨터
    tick = -1
    count = [0 for _ in range(N)]
    answer = [0]            # 0번은 갯수 1번부터 사용한 횟수
    
    while time_set:
        tick += 1
        if tick == time_set[0][START]:
            temp = time_set.popleft()
            idx = heapq.heappop(empty)
            heapq.heappush(computers, [temp[END], temp[START], idx])
            count[idx] += 1
        if computers and computers[0][0] == tick:
            s, e, i = heapq.heappop(computers)
            heapq.heappush(empty, i)
    
    for i in count:
        if i == 0:
            break
        else:
            answer.append(i)
            answer[0] += 1
    
    print(answer[0])
    print(*answer[1:])
    

    변수가 많아서 조금 복잡하지만 중요한건 heap을 이용하여 비어있는 컴퓨터와 종료하는 사람을 찾아
    O(N^2)으로 풀지 않는 것이 핵심인것 같다. 

     

    'Algorithm Study > Python' 카테고리의 다른 글

    [백준] 21275번 폰 호석만  (0) 2021.06.28
    [백준] 1012번 유기농 배추  (0) 2021.05.31
    [백준] 21737번 SMUPC계산기  (0) 2021.05.28
    [백준] 1520번 내리막 길  (0) 2021.05.28
    [백준] 1092번 배  (0) 2021.05.28

    댓글

From BlackHair