본문 바로가기
알고리즘 문제풀이/프로그래머스 고득점 Kit

[프로그래머스 고득점 Kit - 힙] 2. 디스크 컨트롤러

by summer_light 2023. 10. 1.

[프로그래머스 고득점 Kit - 힙] 디스크 컨트롤러

 

문제 설명
하드디스크는 한 번에 하나의 작업만 수행할 수 있습니다. 디스크 컨트롤러를 구현하는 방법은 여러 가지가 있습니다. 가장 일반적인 방법은 요청이 들어온 순서대로 처리하는 것입니다.

예를들어

- 0ms 시점에 3ms가 소요되는 A작업 요청
- 1ms 시점에 9ms가 소요되는 B작업 요청
- 2ms 시점에 6ms가 소요되는 C작업 요청
와 같은 요청이 들어왔습니다. 이를 그림으로 표현하면 아래와 같습니다.


한 번에 하나의 요청만을 수행할 수 있기 때문에 각각의 작업을 요청받은 순서대로 처리하면 다음과 같이 처리 됩니다.

- A: 3ms 시점에 작업 완료 (요청에서 종료까지 : 3ms)
- B: 1ms부터 대기하다가, 3ms 시점에 작업을 시작해서 12ms 시점에 작업 완료(요청에서 종료까지 : 11ms)
- C: 2ms부터 대기하다가, 12ms 시점에 작업을 시작해서 18ms 시점에 작업 완료(요청에서 종료까지 : 16ms)
이 때 각 작업의 요청부터 종료까지 걸린 시간의 평균은 10ms(= (3 + 11 + 16) / 3)가 됩니다.

하지만 A → C → B 순서대로 처리하면

- A: 3ms 시점에 작업 완료(요청에서 종료까지 : 3ms)
- C: 2ms부터 대기하다가, 3ms 시점에 작업을 시작해서 9ms 시점에 작업 완료(요청에서 종료까지 : 7ms)
- B: 1ms부터 대기하다가, 9ms 시점에 작업을 시작해서 18ms 시점에 작업 완료(요청에서 종료까지 : 17ms)
이렇게 A → C → B의 순서로 처리하면 각 작업의 요청부터 종료까지 걸린 시간의 평균은 9ms(= (3 + 7 + 17) / 3)가 됩니다.

각 작업에 대해 [작업이 요청되는 시점, 작업의 소요시간]을 담은 2차원 배열 jobs가 매개변수로 주어질 때, 작업의 요청부터 종료까지 걸린 시간의 평균을 가장 줄이는 방법으로 처리하면 평균이 얼마가 되는지 return 하도록 solution 함수를 작성해주세요. (단, 소수점 이하의 수는 버립니다)

제한 사항
jobs의 길이는 1 이상 500 이하입니다.
jobs의 각 행은 하나의 작업에 대한 [작업이 요청되는 시점, 작업의 소요시간] 입니다.
각 작업에 대해 작업이 요청되는 시간은 0 이상 1,000 이하입니다.
각 작업에 대해 작업의 소요시간은 1 이상 1,000 이하입니다.
하드디스크가 작업을 수행하고 있지 않을 때에는 먼저 요청이 들어온 작업부터 처리합니다.


입출력 예


입출력 예 설명
문제에 주어진 예와 같습니다.

0ms 시점에 3ms 걸리는 작업 요청이 들어옵니다.
1ms 시점에 9ms 걸리는 작업 요청이 들어옵니다.
2ms 시점에 6ms 걸리는 작업 요청이 들어옵니다.


[내 풀이 - 실패]

- 최초 풀이 약 50분, while 문 뼈대를 뭐로 세울지 헷갈렸다. : 코드 부터 작성하지 않고 글로 작성하니 바로 풀렸다. 

- 그리고 11,17,20 빼고 모두 실패

import heapq as hq
def solution(jobs):
    # 걸린 시간을 최소로 하려면, 당장 실행 가능한 프로그램들 중에서 제일 짧은 프로그램을 먼저 처리하면 된다.
    length = len(jobs)
    currentTime = jobs[0][0]
    totalTime = 0
    heap = []
    hq.heappush(heap, (jobs[0][1], jobs[0][0]))
    jobs.pop(0)
    while heap:
        # 힙에 넣어진 작업을 하나 수행
        pT, gT = hq.heappop(heap)

        # 현재 시간과 totalTime 다시 계산
        totalTime += pT + (currentTime - gT)
        currentTime += pT

        # 만약 힙이 비어있어서, 멈추게 되는 경우 다음 작업의 getTime 까지 이동
        if not heap and jobs and currentTime < jobs[0][0]:
            currentTime = jobs[0][0]

        # 현재 시간보다 더 이른 getTime을 가진 job들은 모두 힙에 넣기
        while jobs and jobs[0][0] <= currentTime:
            getTime, playTime = jobs.pop(0)
            hq.heappush(heap, (playTime, getTime))
    return (totalTime) // length

 

[내 풀이2 - 성공]

- 오류 고치는 데 12분.

- 알고리즘 자체에 문제가 있었던 것은 아니었고, 주어진 값이 정렬되어 들어온다는 말이 없었는데, 입출력 예가 정렬되어 들어오는 것처럼 보여서 입력값 정렬을 하지 않아 발생한 오류였다. 

- jobs.sort() 이 것만 추가해서 해결. 

- 실제 코딩테스트에서도 공개된 테스트케이스 만의 특성인지, 실제 조건이 그런지 항상 체크하도록 하자. 한 줄 때문에 정확도 테스트에서 실패하면 너무 아깝다... 항상 경계값, 입력값의 조건 등 체크하는 것을 습관들이자. 테스트 케이스만 통과하고 본 케이스에서 실패하는 케이스가 너무 많다ㅠㅠ

import heapq as hq
def solution(jobs):
    # 걸린 시간을 최소로 하려면, 당장 실행 가능한 프로그램들 중에서 제일 짧은 프로그램을 먼저 처리하면 된다.
    jobs.sort()
    length = len(jobs)
    currentTime = jobs[0][0]
    totalTime = 0
    heap = []
    hq.heappush(heap, (jobs[0][1], jobs[0][0]))
    jobs.pop(0)
    while heap:
        # 힙에 넣어진 작업을 하나 수행
        pT, gT = hq.heappop(heap)

        # 현재 시간과 totalTime 다시 계산
        totalTime += pT+(currentTime-gT)
        currentTime += pT

        # 만약 힙이 비어있어서, 멈추게 되는 경우 다음 작업의 getTime 까지 이동
        if not heap and jobs and currentTime < jobs[0][0]:
            currentTime = jobs[0][0]
        
        # 현재 시간보다 더 이른 getTime을 가진 job들은 모두 힙에 넣기
        while jobs and jobs[0][0] <= currentTime:
            getTime, playTime = jobs.pop(0)
            hq.heappush(heap, (playTime, getTime))   
    return (totalTime)//length

[내 풀이3 - 성공(리팩토링)]

import heapq as hq
def solution(jobs):
    # 걸린 시간을 최소로 하려면, 당장 실행 가능한 프로그램들 중에서 제일 짧은 프로그램을 먼저 처리하면 된다.
    jobs.sort(reverse=True)
    length = len(jobs)
    
    currentTime = jobs[-1][0]
    totalTime = 0
    
    heap = []
    hq.heappush(heap, (jobs[-1][1], jobs[-1][0]))
    jobs.pop()
    while heap:
        # 힙에 넣어진 작업을 하나 수행
        pT, gT = hq.heappop(heap)

        # 현재 시간과 totalTime 다시 계산
        totalTime += pT+(currentTime-gT)
        currentTime += pT

        # 만약 힙이 비어있어서, 멈추게 되는 경우 다음 작업의 getTime 까지 이동
        if not heap and jobs and currentTime < jobs[-1][0]:
            currentTime = jobs[-1][0]
        
        # 현재 시간보다 더 이른 getTime을 가진 job들은 모두 힙에 넣기
        while jobs and jobs[-1][0] <= currentTime:
            getTime, playTime = jobs.pop()
            hq.heappush(heap, (playTime, getTime))   
    return (totalTime)//length

[대표 풀이]

- 이 분은 current_time = max(current_time + dur, arr + dur) 를 통해, 마지막 if 문에서 실행 가능한 시점에 들어오지 않은 task의 경우에 current_time이 arr + dur 이 되는 것을 max() 으로 처리했다.  

- 하지만 while 문에 들어오는 task의 종류가 당장 실행 가능한 task만 들어오는 것이 아니라는 게 일관성이 떨어지는 것 같긴하다. 살짝 직관성이 부족한 느낌.

- 이 부분은 내 풀이가 살짝 더 긴 코드를 요구하지만, 더 직관적인 것 같다(힙이 비어 있는 상태에서 더 이상 수행 가능한 job이 없는 경우에는 currentTime을 점프해주는 것으로 표현)

import heapq
from collections import deque

def solution(jobs):
    tasks = deque(sorted([(x[1], x[0]) for x in jobs], key=lambda x: (x[1], x[0])))
    q = []
    heapq.heappush(q, tasks.popleft())
    current_time, total_response_time = 0, 0
    while len(q) > 0:
        dur, arr = heapq.heappop(q)
        current_time = max(current_time + dur, arr + dur)
        total_response_time += current_time - arr
        while len(tasks) > 0 and tasks[0][1] <= current_time:
            heapq.heappush(q, tasks.popleft())
        if len(tasks) > 0 and len(q) == 0:
            heapq.heappush(q, tasks.popleft())
    return total_response_time // len(jobs)

 

 

 

 

 

 

 

댓글