無限

몸은 현실 마음은 낭만

Develop & Journal

코딩테스트 연습

프로그래머스 디스크 컨트롤러

알파 조 2024. 3. 21. 21:49
728x90
반응형
SMALL

https://school.programmers.co.kr/learn/courses/30/lessons/42627

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

문제 설명

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

예를들어

  • `0ms 시점에 3ms가 소요되는 A작업 요청
  • 1ms 시점에 9ms가 소요되는 B작업 요청
  • 2ms 시점에 6ms가 소요되는 C작업 요청`

와 같은 요청이 들어왔습니다. 이를 그림으로 표현하면 아래와 같습니다.

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

이 때 각 작업의 요청부터 종료까지 걸린 시간의 평균은 10ms(= (3 + 11 + 16) / 3)가 됩니다.

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

이렇게 A → C → B의 순서로 처리하면 각 작업의 요청부터 종료까지 걸린 시간의 평균은 9ms(= (3 + 7 + 17) / 3)가 됩니다.

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

제한 사항

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

알고리즘

  1. 작업을 요청 시간을 기준으로 오름차순으로 정렬합니다.
  2. 현재 시간부터 처리할 작업을 선택합니다. 초기에는 현재 시간은 0입니다.
  3. 현재 시간보다 이전에 요청된 작업들을 우선순위 큐에 넣습니다.
  4. 우선순위 큐에서는 작업이 끝나는 시간이 빠른 순서로 작업이 정렬됩니다.
  5. 우선순위 큐가 비어있으면 현재 시간을 다음 작업의 요청 시간으로 이동합니다.
  6. 우선순위 큐에서 작업이 끝나는 시간이 가장 빠른 작업을 선택하여 처리합니다.
  7. 작업이 끝나면 해당 작업의 요청부터 종료까지 걸린 시간을 누적합니다.
  8. 현재 시간을 해당 작업이 끝나는 시간으로 갱신합니다.
  9. 이 작업을 모든 작업이 처리될 때까지 반복합니다.

 

코드

#include <string>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;

struct Compare {
    bool operator()(const pair<int, int>& a, const pair<int, int>& b) {
        return a.second > b.second; // 작업이 끝나는 시간이 빠른 순서대로 정렬
    }
};

int solution(vector<vector<int>> jobs) {
    int answer = 0;
    int currentTime = 0; // 현재 시간을 나타내는 변수
    int jobIndex = 0; // 처리할 작업의 인덱스
    priority_queue<pair<int, int>, vector<pair<int, int>>, Compare> pq;

    sort(jobs.begin(), jobs.end());

    while (jobIndex < jobs.size() || !pq.empty()) {
        while (jobIndex < jobs.size() && jobs[jobIndex][0] <= currentTime) {
            pq.push(make_pair(jobs[jobIndex][0], jobs[jobIndex][1]));
            ++jobIndex;
        }
        
        if (pq.empty()) {
            currentTime = jobs[jobIndex][0];
        } else {
            pair<int, int> job = pq.top();
            pq.pop();
            answer += currentTime - job.first + job.second;
            currentTime += job.second;
        }
    }

    return answer / jobs.size();
}

struct Compare {
    bool operator()(const pair<int, int>& a, const pair<int, int>& b) {
        return a.second > b.second; // 작업이 끝나는 시간이 빠른 순서대로 정렬
    }
};

Compare 구조체를 정의하여 우선순위 큐에 사용될 비교 연산자를 정의합니다. 이 연산자는 작업이 끝나는 시간이 빠른 순서대로 정렬되도록 설정됩니다.

 

cppCopy code
int solution(vector<vector<int>> jobs) {
    int answer = 0;
    int currentTime = 0; // 현재 시간을 나타내는 변수
    int jobIndex = 0; // 처리할 작업의 인덱스
    priority_queue<pair<int, int>, vector<pair<int, int>>, Compare> pq;

    // 작업 요청 시간을 기준으로 오름차순으로 정렬
    sort(jobs.begin(), jobs.end());

solution 함수를 정의합니다. 여기서 **answer**는 작업의 평균 시간을 저장할 변수이고, **currentTime**는 현재 시간을 나타내는 변수입니다. **jobIndex**는 처리할 작업의 인덱스를 나타내며, **pq**는 우선순위 큐입니다. 작업을 요청 시간을 기준으로 오름차순으로 정렬합니다.

 

cppCopy code
    while (jobIndex < jobs.size() || !pq.empty()) {
        // 현재 시간 이전에 요청된 작업들을 우선순위 큐에 넣음
        while (jobIndex < jobs.size() && jobs[jobIndex][0] <= currentTime) {
            pq.push(make_pair(jobs[jobIndex][0], jobs[jobIndex][1]));
            ++jobIndex;
        }

while 루프를 사용하여 아직 처리하지 않은 작업이 남아 있거나 우선순위 큐에 대기 중인 작업이 있는 동안에 계속해서 작업을 처리합니다. 이 안에서 현재 시간 이전에 요청된 작업들을 우선순위 큐에 넣습니다.

 

cppCopy code
        // 우선순위 큐가 비어있다면 대기 중인 작업이 없으므로 시간을 넘김
        if (pq.empty()) {
            currentTime = jobs[jobIndex][0];
        } else {
            // 작업이 끝나는 시간이 가장 빠른 작업을 꺼내어 처리
            pair<int, int> job = pq.top();
            pq.pop();
            answer += currentTime - job.first + job.second; // 각 작업의 요청부터 종료까지 걸린 시간 계산
            currentTime += job.second; // 현재 시간을 갱신
        }
    }

만약 우선순위 큐가 비어있다면 현재 대기 중인 작업이 없으므로 시간을 넘깁니다. 그렇지 않다면 우선순위 큐에서 작업이 끝나는 시간이 가장 빠른 작업을 꺼내어 처리합니다.

pair<int, int> job = pq.top(); 이 부분은 우선순위 큐에서 작업이 끝나는 시간이 가장 빠른 작업을 가져오기 위해 사용됩니다.

  • **pq.top()**을 호출하면 현재 우선순위 큐에서 가장 작은(끝나는 시간이 가장 빠른) 작업이 반환됩니다.
  • 반환된 작업은 pair<int, int> 형식으로, 첫 번째 요소는 작업이 요청된 시간이고 두 번째 요소는 작업의 소요 시간입니다.
  • 이 작업을 **job**이라는 변수에 저장하여 이후 작업을 처리하는 데 사용됩니다.

따라서 pair<int, int> job = pq.top(); 구문은 우선순위 큐에서 가장 빠른 작업을 가져와서 job 변수에 저장하는 것입니다. 이후 이 작업을 처리하고 현재 시간을 갱신하는 데 사용됩니다.

 

cppCopy code
    return answer / jobs.size(); // 평균 계산
}

마지막으로 작업의 평균 시간을 계산하고 반환합니다.

 

 

주요한 포인트

  1. 우선순위 큐를 활용한 작업 스케줄링: 작업을 우선순위 큐에 넣어 작업이 끝나는 시간이 가장 빠른 순서대로 처리됩니다. 이렇게 하면 최소 대기 시간을 갖는 스케줄을 구현할 수 있습니다.
  2. 작업 처리 루프: 주어진 작업을 모두 처리하거나 우선순위 큐가 비어있을 때까지 계속해서 작업을 처리합니다. 이때 작업의 요청 시간이 현재 시간 이전이라면 해당 작업을 우선순위 큐에 넣어 대기합니다.
  3. 평균 계산: 모든 작업의 처리 시간을 누적한 후에는 작업의 수로 나누어 평균 처리 시간을 계산합니다.
반응형
LIST

'코딩테스트 연습' 카테고리의 다른 글

프로그래머스 베스트앨범  (1) 2024.03.22
프로그래머스 더맵게  (0) 2024.03.21
프로그래머스 모음사전  (0) 2024.03.20
프로그래머스 피로도  (0) 2024.03.20
프로그래머스 카펫  (0) 2024.03.19