현대모비스는 우수한 SW 인재 채용을 위해 상시로 채용 설명회를 진행하고 있습니다. 채용 설명회에서는 채용과 관련된 상담을 원하는 참가자에게 멘토와 1:1로 상담할 수 있는 기회를 제공합니다. 채용 설명회에는 멘토 n
명이 있으며, 1~k
번으로 분류되는 상담 유형이 있습니다. 각 멘토는 k
개의 상담 유형 중 하나만 담당할 수 있습니다. 멘토는 자신이 담당하는 유형의 상담만 가능하며, 다른 형의 상담은 불가능합니다. 멘토는 동시에 참가자 한 명과만 상담 가능하며, 상담 시간은 정확히 참가자가 요청한 시간만큼 걸립니다.
참가자가 상담 요청을 하면 아래와 같은 규칙대로 상담을 진행합니다.
참가자의 상담 요청 정보가 주어질 때, 참가자가 상담을 요청했을 때부터 상담을 시작하기까지 기다린 시간의 합이 최소가 되도록 각 상담 유형별로 멘토 인원을 정하려 합니다. 단, 각 유형별로 멘토 인원이 적어도 한 명 이상이어야 합니다.
예를 들어, 5명의 멘토가 있고 1~3번의 3가지 상담 유형이 있을 때 아래와 같은 참가자의 상담 요청이 있습니다.
참가자의 상담 요청
참가자 번호 | 시각 | 상담 시간 | 상담 유형 |
---|---|---|---|
1번 참가자 | 10분 | 60분 | 1번 유형 |
2번 참가자 | 15분 | 100분 | 3번 유형 |
3번 참가자 | 20분 | 30분 | 1번 유형 |
4번 참가자 | 30분 | 50분 | 3번 유형 |
5번 참가자 | 50분 | 40분 | 1번 유형 |
6번 참가자 | 60분 | 30분 | 2번 유형 |
7번 참가자 | 65분 | 30분 | 1번 유형 |
8번 참가자 | 70분 | 100분 | 2번 유형 |
이때, 멘토 인원을 아래와 같이 정하면, 참가자가 기다린 시간의 합이 25로 최소가 됩니다.
1번 유형 | 2번 유형 | 3번 유형 |
---|---|---|
2명 | 1명 | 2명 |
1번 유형
1번 유형을 담당하는 멘토가 2명 있습니다.
2번 유형을 담당하는 멘토가 1명 있습니다.
3번 유형
3번 유형을 담당하는 멘토가 2명 있습니다.
상담 유형의 수를 나타내는 정수 k
, 멘토의 수를 나타내는 정수 n
과 참가자의 상담 요청을 담은 2차원 정수 배열reqs
가 매개변수로 주어집니다. 멘토 인원을 적절히 배정했을 때 참가자들이 상담을 받기까지 기다린 시간을 모두 합한 값의 최솟값을 return 하도록 solution 함수를 완성해 주세요.
k
≤ 5n
≤ 20reqs
의 길이 ≤ 300reqs
의 원소는 [a
, b
, c
] 형태의 길이가 3인 정수 배열이며, c
번 유형의 상담을 원하는 참가자가 a
분에 b
분 동안의 상담을 요청했음을 의미합니다.a
≤ 1,000b
≤ 100c
≤ k
reqs
는 a
를 기준으로 오름차순으로 정렬되어 있습니다.reqs
배열에서 a
는 중복되지 않습니다. 즉, 참가자가 상담 요청한 시각은 모두 다릅니다.k | n | reqs | result |
---|---|---|---|
3 | 5 | [[10, 60, 1], [15, 100, 3], [20, 30, 1], [30, 50, 3], [50, 40, 1], [60, 30, 2], [65, 30, 1], [70, 100, 2]] | 25 |
2 | 3 | [[5, 55, 2], [10, 90, 2], [20, 40, 2], [50, 45, 2], [100, 50, 2]] | 90 |
입출력 예 #1
문제 예시와 같습니다.
입출력 예 #2
참가자의 상담 요청
참가자 번호 | 시각 | 상담 시간 | 상담 유형 |
---|---|---|---|
1번 참가자 | 5분 | 55분 | 1번 유형 |
2번 참가자 | 10분 | 90분 | 1번 유형 |
3번 참가자 | 20분 | 40분 | 1번 유형 |
4번 참가자 | 50분 | 45분 | 1번 유형 |
5번 참가자 | 100분 | 50분 | 1번 유형 |
멘토 인원을 아래와 같이 정하면, 참가자가 기다린 시간의 합이 90으로 최소가 됩니다.
1번 유형 | 2번 유형 |
---|---|
1명 | 2명 |
1번 유형
1번 유형을 담당하는 멘토가 1명 있습니다. 1번 유형의 상담을 요청한 참가자가 없지만, 유형별로 멘토 인원이 적어도 한 명 이상이어야 합니다.
2번 유형
2번 유형을 담당하는 멘토가 2명 있습니다.
따라서 90을 return 하면 됩니다.
// 각 유형별 상담 요청 배열과, 해당 유형의 상담원 수를 입력으로 받아 총 대기시간을 구하는 함수
const getTime = (reqArray, consultNum) => {
let currentTime = 0; // 현재 시간을 담는 변수
let waitTimeSum = 0; // 총 대기시간을 담는 변수
let index = 0; // 현재 대기중인 사람중 가장 먼저 상담을 받을 사람의 인덱스
const consultEndArray = []; // 현재 상담을 받는 사람의 종료 시각을 담은 배열
// 마지막 사람이 상담을 받기 시작한 순간 더이상의 대기는 발생하지 않게 됨
while (index < reqArray.length) {
// 상담이 종료된 순간 consultEndArray에서 해당 상담 종료시각값을 제거함
while (consultEndArray.length > 0) {
if (currentTime >= consultEndArray[consultEndArray.length - 1]) consultEndArray.pop();
else break;
}
// 상담시작 시각과 상담 시간 가져오기
const [a, b] = reqArray[index];
// 상담시간이 되었고, 상담이 가능한 경우에는 상담 종료 시각을 consultEndArray에 넣고 인덱스를 1 올림
if (currentTime >= a && consultEndArray.length < consultNum) {
consultEndArray.push(currentTime + b);
consultEndArray.sort((x, y) => y - x); // 상담종료시각을 담은 배열을 정렬해 pop을 사용할 수 있게 함
index += 1;
}
// 상담시간이 되었지만, 멘토가 모두 상담을 하고 있어 기다려야 하는 상황에는 대기시간을 더해줌
else if (currentTime >= a) {
// 기다리고 있는 사람수 확인
let waitUser = 0;
for (let i = index; i < reqArray.length; i++) {
if (currentTime >= reqArray[i][0]) waitUser++;
else break;
}
// 기다리는 사람수만큼 대기시간을 더하고, 현재시간을 1분만큼 증가시킴
waitTimeSum += waitUser;
currentTime += 1;
}
// 시간을 1분 증가시킴
else currentTime += 1;
}
return waitTimeSum;
}
// 상담사 숫자, 현재 분배 상태를 담은 배열을 받아 멘토를 분배하는 모든 경우의 수를 찾는 함수
const getNumberSet = (divideLength, consultNum, currentArray) => {
// 길이가 1이면 남은 상담원을 한 유형에 전부 집어넣음
if (currentArray.length === divideLength - 1)
return [[...currentArray, consultNum]];
// 남은 사람수와 남은 유형수가 일치하면 남은 유형의 상담원수를 전부 1로 처리하여 반환
else if (divideLength - currentArray.length === consultNum)
return [[...currentArray, ...new Array(divideLength - currentArray.length).fill(1)]];
// 둘다 아닌경우 현재 유형에 배정될 상담원 수를 1부터 가능한 최대치까지 배치한 모든 경우의 수 탐색
else {
const divideSet = [];
// 해당 유형에서 가능한 상담원 수의 최대치는 현재 상담원 수 - 유형수 + 1
for (let i = 1; i <= consultNum - (divideLength - currentArray.length) + 1; i++)
// 재귀함수를 통해 특정 상담원 수를 선택했을 때 모든 경우의 수를 가져오고, 각각을 반환할 배열에 push해줌
getNumberSet(divideLength, consultNum - i, [...currentArray, i]).forEach(e => divideSet.push(e));
return divideSet;
}
}
function solution(k, n, reqs) {
const timeArrayByType = []; // 유형, 멘토수 별로 대기시간을 담을 배열
// timeArrayByType을 채울 로직
for (let i = 1; i <= k; i++) {
// reqs에서 각 타입에 맞는 유형만을 필터링함
const currentReqs = reqs.filter(([a, b, c]) => c === i);
// 멘토 숫자가 0인 경우는 존재하지 않음, 실수할 경우를 대비해 Infinity로 설정
const timeArray = [Infinity];
// 앞서 제작한 함수를 통해 현재 유형에서 멘토 수에 따른 대기시간을 구해 배열에 저장
for(let j = 1; j <= n; j++) timeArray.push(getTime(currentReqs, j));
timeArrayByType.push(timeArray);
}
// 멘토수를 분배하는 모든 경우의 수
const combinationAll = getNumberSet(k, n, []);
// 각 경우의수 별 대기시간의 합 구하기
return combinationAll.map(data => {
let waitTime = 0;
for (let i = 0; i < data.length; i++) {
waitTime += timeArrayByType[i][data[i]]
}
return waitTime;
}).sort((a, b) => a - b)[0];
}
// 각 유형별 상담 요청 배열과, 해당 유형의 상담원 수를 입력으로 받아 총 대기시간을 구하는 함수
const getTime = (reqArray, consultNum) => {
let currentTime = 0; // 현재 시간을 담는 변수
let waitTimeSum = 0; // 총 대기시간을 담는 변수
let index = 0; // 현재 대기중인 사람중 가장 먼저 상담을 받을 사람의 인덱스
const consultEndArray = []; // 현재 상담을 받는 사람의 종료 시각을 담은 배열
// 마지막 사람이 상담을 받기 시작한 순간 더이상의 대기는 발생하지 않게 됨
while (index < reqArray.length) {
// 상담이 종료된 순간 consultEndArray에서 해당 상담 종료시각값을 제거함
while (consultEndArray.length > 0) {
if (currentTime >= consultEndArray[consultEndArray.length - 1]) consultEndArray.pop();
else break;
}
// 상담시작 시각과 상담 시간 가져오기
const [a, b] = reqArray[index];
// 상담시간이 되었고, 상담이 가능한 경우에는 상담 종료 시각을 consultEndArray에 넣고 인덱스를 1 올림
if (currentTime >= a && consultEndArray.length < consultNum) {
consultEndArray.push(currentTime + b);
consultEndArray.sort((x, y) => y - x); // 상담종료시각을 담은 배열을 정렬해 pop을 사용할 수 있게 함
index += 1;
}
// 상담시간이 되었지만, 멘토가 모두 상담을 하고 있어 기다려야 하는 상황에는 대기시간을 더해줌
else if (currentTime >= a) {
// 기다리고 있는 사람수 확인
let waitUser = 0;
for (let i = index; i < reqArray.length; i++) {
if (currentTime >= reqArray[i][0]) waitUser++;
else break;
}
// 기다리는 사람수만큼 대기시간을 더하고, 현재시간을 1분만큼 증가시킴
waitTimeSum += waitUser;
currentTime += 1;
}
// 시간을 1분 증가시킴
else currentTime += 1;
}
return waitTimeSum;
}
// 상담사 숫자, 현재 분배 상태를 담은 배열을 받아 멘토를 분배하는 모든 경우의 수를 찾는 함수
const getNumberSet = (divideLength, consultNum, currentArray) => {
// 길이가 1이면 남은 상담원을 한 유형에 전부 집어넣음
if (currentArray.length === divideLength - 1) return [[...currentArray, consultNum]];
// 남은 사람수와 남은 유형수가 일치하면 남은 유형의 상담원수를 전부 1로 처리하여 반환
else if (divideLength - currentArray.length === consultNum) return [[...currentArray, ...new Array(divideLength - currentArray.length).fill(1)]];
// 둘다 아닌경우 현재 유형에 배정될 상담원 수를 1부터 가능한 최대치까지 배치한 모든 경우의 수 탐색
else {
const divideSet = [];
// 해당 유형에서 가능한 상담원 수의 최대치는 현재 상담원 수 - 유형수 + 1
for (let i = 1; i <= consultNum - (divideLength - currentArray.length) + 1; i++)
getNumberSet(divideLength, consultNum - i, [...currentArray, i]).forEach(e => divideSet.push(e));
return divideSet;
}
}
function solution(k, n, reqs) {
const timeArrayByType = []; // 유형, 멘토수 별로 대기시간을 담을 배열
// timeArrayByType을 채울 로직
for (let i = 1; i <= k; i++) {
// reqs에서 각 타입에 맞는 유형만을 필터링함
const currentReqs = reqs.filter(([a, b, c]) => c === i);
// 멘토 숫자가 0인 경우는 존재하지 않음, 실수할 경우를 대비해 Infinity로 설정
const timeArray = [Infinity];
// 앞서 제작한 함수를 통해 현재 유형에서 멘토 수에 따른 대기시간을 구해 배열에 저장
for(let j = 1; j <= n; j++) timeArray.push(getTime(currentReqs, j));
timeArrayByType.push(timeArray);
}
// 멘토수를 분배하는 모든 경우의 수
const combinationAll = getNumberSet(k, n, []);
// 각 경우의수 별 대기시간의 합 구하기
return combinationAll.map(data => {
let waitTime = 0;
for (let i = 0; i < data.length; i++) {
waitTime += timeArrayByType[i][data[i]]
}
return waitTime;
}).sort((a, b) => a - b)[0];
}
출처:
https://school.programmers.co.kr/learn/courses/30/lessons/214288?language=javascript#