[Algorithm] 주차요금 계산

yongkini ·2022년 5월 25일
0

Algorithm

목록 보기
52/55

프로그래머스 - 주차요금 계산(2022 카카오 블라인드 공채 기출)

수도코드 및 설계 단계

// 12:00 ~
// 1. 문제 이해
// #조건
// - 계산하는 시간 범위 : 00 ~ 23:59
// - 기본시간&기본요금이 있는데 180분 이하면 => 5000원만 과금
//                            초과면  => 5000 + a
// - 180분 '초과'분이 있을 때 => 10분단위로 나누되 나머지가 남으면 => 올림 => 계산(단위요금 적용)
//                                          남지 않으면 => 그대로 계산(단위 요금 적용)
// - 없으면 위에 말한대로 기본요금
// + a
// - [기본시간, 기본요금, 단위시간, 단위요금] => 입력에 따라 정해지는 것으로 항상 5000아님!!**
// - records는 문자열 형태이기에 이걸 parsing해서 값을 나눠서 받아야함
// - 잘못된 시각("25:22", "09:65" 등)은 입력으로 주어지지 않습니다 => 예외처리 필요x
// - 차량번호가 문자열이고, 0으로 시작할 가능성도 있기때문에 이걸 parseInt()로 바꿔서 정렬?로직 만들어야할듯
// - records는 시각을 기준으로 오름차순임.
// + 우려할 예외처리 사항은 특별히 없는듯!

// 2. 문제 설계
// 목표 : 각 차량번호에 매핑되는 과금량을 배열에 담되 차량번호를 기준으로 오름차순해서 리턴
// 로직 : 
// - records의 길이가 크지 않으므로 시간복잡도 관련 문제는 아님
// - 하나하나 카운팅해서 산출하면 될듯. => 
//   - records를 순회하면서 각 데이터를 사람이 노트에 수기하듯이 특정 자료구조에 넣으면서 순회한다
//   - 차량번호를 키로하고 값으로 또다른 객체를 만들어서 (객체로 해야 탐색할때 O(1)임)
// - status : IN or OUT, 
// - inTime : 입장 시간 
// - outTime은 필요없음 => 어차피 두번째로 특정 차량번호 자료가 나오면 과금을 하면서 해당 키값을 없앨것이기에
// 그렇게 자료를 저장하면서 순회하다가
// 똑같은 자료가(차량번호) 나왔다? => outTime(현재 탐색중인 자료에 있는 시간) - inTime 시간을 계산함(분) 
// A 먼저 받아놓은 기본시간보다 체류시간이 길다? => 추가 요금 계산(기본 시간을 빼고 남은 시간에 단위로 나눠서계산)
// B 적다 => 그대로 기본 요금으로 계산 

// ** 과금량을 배열에 담을 때 returnArr[parseInt(차량번호)] = 과금량(대신 returnArr.length = 1000 max값으로 해두기)
// 이런식으로 해두고 추가를 하고 나중에 undefined만 제거해주면 원하는대로 만들 수 있을듯!

// 탐색을 마친 다음 끝이 아니라 객체(수기로 적는것을 따라한 로직에서 만든)에 남아있는 정보가 있는지 확인
// 만약 남아 있지 않으면 그대로 끝
// 남아있으면 그 정보에서 inTime을 23:59에서 빼서 저 위에 로직 그대로 적용후 똑같이 배열에 추가
// 차량 체류시간 로직은 함수로 만드는게 좋을듯
// 이렇게 풀면 완벽할 것 같다.

// 18분 걸림

// 53분 마무리(12:53)

// - answer의 length를 최대값인 1000으로 해주자
// - records에 대해서 for문으로 탐색 이 때 딱히 index가 필요없으로 of 문을쓰자
// records 의 record에 대해서 값을 변수로 받자(헷갈리지 않게) + 파싱 로직
// record = "05:34 5961 IN" (ex)
// const splitRecord = record.split(' ');
// const inTime = splitRecord[0];
// const carNum = splitRecord[1];
// const status = splitRecord[2]; 
// 이게 IN하는 차인지 OUT하는 차인지를 status 파악후에
// IN => 그대로 아까말한 객체(carStatus)에 차량번호를 키값 + 객체 만들기
// OUT => 함수 적용해서 과금량 측정 
        // args = Number

// 함수를 만드는데 
// - inTime, outTime, defaultPrice, defaultTime, plusPrice, plusTime (argus)
// - outTime - inTime (a-b라고 가정)
// - 먼저 ":" 기준으로 split해서

// 분끼리 계산 -> 만약 a의 분이 더크다 => 그대로 a-b
// -> a의 분값이 더 작다 -> (60-b) + a & flag = true
// -> 그다음으로 시간계산하는데 여기서는 무조건 뒤에 a가 큼(그런 예외는 없다고했음)
// -> a-b 하되  flag = true면 그 값에 -1 해줌
// 그럼 체류시간(분)이(=stayTime) 나옴 => 그 값을 바탕으로 
// => defaultTime-stayTime >= 0 => defaultPrice 끝
// 5:34 8 :10 2:36
// => < 0 => 그 뺀시간(분)만큼을 plusTime으로 나누고+올림 => 그 값을 plusPrice로 곱해준값 끝
// + 이미 OUT된 차량이므로 carStaus에서 delete
// 함수에서 리턴받은 과금량을 아까 말한 방법으로 answer에 추가
// for문이 끝나고 carStatus.keys().length === 0 인지 체크
// ===0이면끝// 아니면 마지막 남은 값에 outTime을 23:59로해서 같은 로직 돌리기
// 마지막으로 filter써서 undefined제거하고 리턴
// 35분걸림

// #매개변수
// - fees : 주차 요금을 나타내는 정수배열(array<Number>)
// - records : 입출차 내역(array<String>)

// 출력
// - 차량 번호가 작은순으로 과금량을(Number T) 배열에 담아서 리턴

// 회고
// 생각해보니 inTime밖에 필요없고, 아예 여기서 분으로 환산해서 계산해버리자(error) **
// 일괄 계산 로직을 조건에서 잘읽어놓고 간과하고 설계함(error) **

실제 작성 코드

function solution(fees, records) {
    var answer = [];
// 2 -(2)
// 실제 코드설계
// - fees를 받아서 원하는 변수에 넣어놓자
    const defaultTime = fees[0];
    const defaultPrice = fees[1];
    const plusTime = fees[2];
    const plusPrice = fees[3];
    const carStatus = {};
// defaultTime/defaultPrice/plusTime/plusPrice 이렇게 받자
// records 는 그대로 받고
    answer.length = 1000;
    
    const getTotalMin = (time,carNum) => {
        let totalMin, totalHour;
        let flag = false;
        const [aHour, aMin] = time.split(":");
        const [bHour, bMin] = carStatus[carNum].split(":");
        const aMinNum = parseInt(aMin);
        const bMinNum = parseInt(bMin);
        if(aMinNum >= bMinNum) totalMin = aMinNum - bMinNum;
        else {
            totalMin = (60-bMinNum) + aMinNum
            flag = true;
        }
        if(flag) totalHour = aHour - bHour - 1;
        else totalHour = aHour - bHour;
        totalMin += totalHour * 60;
        return totalMin;
    }
    
    const getTotalPrice = (totalMin) => {
        const stayTime = totalMin - defaultTime;
        if(stayTime < 0) return defaultPrice;
        else {
            return defaultPrice + (Math.ceil(stayTime/plusTime) * plusPrice)
        }
    }
    
    for(let record of records){
        const splitRecord = record.split(' ');
        const time = splitRecord[0];
        const carNum = splitRecord[1];
        const status = splitRecord[2]; 

        if(status === "IN") {

            carStatus[carNum] = time;
        } else if(status === "OUT") {
            const totalMin = getTotalMin(time, carNum);
            if(answer[parseInt(carNum)] !== undefined) answer[parseInt(carNum)]  += totalMin;
            else answer[parseInt(carNum)] = totalMin;
            delete carStatus[carNum];
        }
    }
    
    if(Object.keys(carStatus).length !== 0) {
        for (let carNum of Object.keys(carStatus)) {
            const totalMin = getTotalMin("23:59", carNum);
            if(answer[parseInt(carNum)] !== undefined) answer[parseInt(carNum)]  += totalMin;
            else answer[parseInt(carNum)] = totalMin;
        }
    }
    return answer.filter(el=>el!==undefined).map(el => {
        if(el <= defaultTime) return defaultPrice;
        else {
            return defaultPrice + (Math.ceil((el-defaultTime)/plusTime) * plusPrice);
        }
    })
}

: 그렇게 효율적인 코드인지는 모르겠지만 시간복잡도 관련 문제는 아니었어서 구현(시뮬레이션) 문제라고 생각하고 분기처리에 집중했다. 사실 작년 공채 시험을 봤었기에 알고 있던 문제긴 했다. 당시에 이문제는 굉장히 쉽게 풀었던 것 같은데 확실히,, 코딩테스트도 안풀어 버릇하면 그 감(?)이 떨어지는건 맞는 것 같다.. 어쨌든 이 문제의 포인트는

  • 객체(해쉬테이블과 같은 O(1)의 시간복잡도로 조회를 할 수 있는 JS의 자료구조)의 키(key)로 차량번호를 저장하고, 키에 대한 값으로 특정 차량이 IN한 시간을 받는다는 아이디어
  • 05:30 & 07:20 등의 시간을 받아서 둘 사이의 경과시간(분단위)을 알아낼 수 있는지

이정도가 아닐까 생각이든다.

profile
완벽함 보다는 최선의 결과를 위해 끊임없이 노력하는 개발자

0개의 댓글