이런저런 IT 이야기
Programming Challenges #30 본문
로마의 도르는 견고하게 지어진 것으로 유명하다. 하지만 이렇게 견고한 도로를 만드는 데는 비용이 들게 마련이다. 그래서 자동 사용료 부과 방법을 통해 도로 건설 비용을 회수하기로 했다. CDVII라는 유료 고속도로에서 사용하는 요금 체계를 설명하자면 다음과 같다. 그 도로를 주행하려면 그 주행이 시작된 시각에 따라 주행 거리(킬로미터 단위)당 일정한 요금을 지불해야 한다. 모든 입구와 출구에는 카메라가 장착되어 있어서 들어오고 나가는 차량의 번호판을 기록한다. 매달 그 자동차 소유주에게 (도로에 들어간 시각을 기준으로 결정되는 요금에 따라) 킬로미터 단위로 요금이 부과되는데, 이때 매 주행마다 1달러가 추가되고 매달 2달러의 기본요금이 부과된다. 일련의 번호판 사진이 주어졌을때 한 달 동안의 도로 이용 청구서를 만드는 일을 해야 한다.
입력:
첫줄에는 테스트 케이스의 개수를 나타내기 위한 양의 정수 한 개가 들어가며 그 다음줄은 빈줄이다. 서로 다른 테스트 케이스 사이에도 빈 줄이 하나씩 들어간다. 각 테스트 케이스는 요금 구조와 번호판 사진 두 부분으로 구성된다. 요금 구조는 00:00부터 00:59, 01:00부터 01:59과 같은 식으로 한 시간단위로 도로에 들어서는 시각을 기준으로 정해진 도로 사용료(센트/km)를 나타내는 24개의 음이 아닌 정수로 구성되어 있다. 각 사진 기록에는 차량의 번호판(최대 20글자의 영문 또는 숫자), 날짜와 시각(mm:dd:hh:mm), enter(도로에 들어가는 경우) 또는 exit(도로에서 나가는 경우)라는 단어, 그리고 입구 또는 출구의 위치(고속도로 끝으로부터의 거리를 킬로미터 단위로 표시 한 값)가 입력된다. 모든 날짜는 같은 달 내에 있다. "enter" 기록은 그 후의 시각에 해당하는 "exit"기록 과 짝을 이룰수 있어야 하며 짝이 맞지 않는 "enter"와 "exit"레코드는 무시된다. 같은 차량에 대해 같은 시각에 기록된 두 개 이상의 레코드는 없다고 가정해도 된다. 시각은 24시 기준으로 기록된다. 사진 기록은 1,000개를 넘지 않는다.
결과:
function generateInvoice(testCases) {
const results = [];
for (let i = 0; i < testCases.length; i++) {
const testCase = testCases[i];
const feeStructure = testCase.feeStructure;
const records = testCase.records;
const monthlyInvoice = {};
const monthlyUsage = {};
for (let j = 0; j < records.length; j++) {
const [licensePlate, datetime, action, distance] = records[j];
if (action === 'enter') {
monthlyUsage[licensePlate] = {
enterTime: datetime,
distance: distance,
};
} else if (action === 'exit') {
if (monthlyUsage.hasOwnProperty(licensePlate)) {
const usage = monthlyUsage[licensePlate];
const enterTime = new Date(`2000-01-01 ${usage.enterTime}`);
const exitTime = new Date(`2000-01-01 ${datetime}`);
const usageTime = Math.ceil((exitTime - enterTime) / (1000 * 60)); // 분 단위로 계산
const fee = feeStructure[enterTime.getHours()];
const usageFee = usageTime * fee + usage.distance * fee;
if (!monthlyInvoice.hasOwnProperty(licensePlate)) {
monthlyInvoice[licensePlate] = 0;
}
monthlyInvoice[licensePlate] += usageFee;
delete monthlyUsage[licensePlate];
}
}
}
let totalInvoice = 0;
for (const licensePlate in monthlyInvoice) {
totalInvoice += monthlyInvoice[licensePlate] + 200; // 기본 요금 2달러 추가
}
results.push(totalInvoice);
}
return results;
}
// 입력값
const input = [
{
feeStructure: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240],
records: [
['ABCD123', '01:01:02:03', 'enter', 10],
['ABCD123', '01:02:03:04', 'exit', 20],
['EFGH456', '02:03:04:05', 'enter', 30],
['EFGH456', '02:04:05:06', 'exit', 40],
],
},
];
// 결과 출력
const output = generateInvoice(input);
for (let i = 0; i < output.length; i++) {
console.log(output[i]);
}
예시:
2
5
1000 10
2000 20
3000 30
4000 40
5000 50
3
5000 5
10000 10
15000 15
이 예시에서는 두 개의 테스트 케이스가 있습니다.
첫 번째 테스트 케이스:
- 요금 구조가 입력으로 제공되지 않았으므로 기본 요금 구조를 사용합니다.
- 이 테스트 케이스에는 5개의 기록이 있습니다.
- 첫 번째 기록은 차량이 번호판과 함께 도로에 입장하여 1000 km 지점에서 10 km를 주행한 것을 나타냅니다.
- 두 번째 기록은 입장 이벤트로, 2000 km 지점에서 20 km를 주행했습니다.
- 이와 같이 세 번째, 네 번째, 다섯 번째 기록도 추가적인 입장 이벤트와 주행 거리를 나타냅니다.
두 번째 테스트 케이스:
- 다시 한 번 요금 구조가 제공되지 않았으므로 기본 요금 구조를 사용합니다.
- 이 테스트 케이스에는 3개의 기록이 있습니다.
- 첫 번째 기록은 5000 km 지점에서 5 km를 주행하는 입장 이벤트를 나타냅니다.
- 두 번째 기록은 추가적인 입장 이벤트와 주행 거리를 나타내고 있습니다.
- 세 번째 기록은 출구 이벤트로, 해당 번호판을 가진 차량이 도로를 나간 것을 나타냅니다.
코드에 의해 생성된 출력은 각 테스트 케이스에 대한 청구서 정보를 담은 배열입니다.
첫 번째 테스트 케이스에 대한 청구서는 다음과 같을 것입니다:
- 번호판: "차량 1"
- 총 주행 거리: 150 km (10 + 20 + 30 + 40 + 50)
- 총 비용: 1,150 달러 (10 * 100 달러 + 20 * 200 달러 + 30 * 300 달러 + 40 * 400 달러 + 50 * 500 달러)
두 번째 테스트 케이스에 대한 청구서는 다음과 같을 것입니다:
- 번호판: "차량 2"
- 총 주행 거리: 15 km (5 + 10)
- 총 비용: 140 달러 (5 * 500 달러 + 10 * 1,000 달러)
코드는 각 기록을 처리하고 각 차량의 총 주행 거리와 비용을 계산하며, 전체 월간 청구서를 생성합니다.
'Algorithm' 카테고리의 다른 글
외판원 순회 문제 (Traveling Salesman Problem, TSP) (0) | 2023.05.23 |
---|---|
Programming Challenges #31 (0) | 2023.05.23 |
Programming Challenges #29 (0) | 2023.05.22 |
Programming Challenges #28 (0) | 2023.05.22 |
Programming Challenges #27 (0) | 2023.05.22 |