프로그래밍/문제풀이

[코딩테스트] 2022 KAKAO BLIND RECRUITMENT - 주차 요금 계산

Churnobyl 2023. 7. 20. 10:44
728x90
반응형


문제

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

 

프로그래머스

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

programmers.co.kr

 

주차장의 요금표와 차량 입출차 기록이 주어졌을 때 차량 별로 주차 요금을 계산하는 문제

 

기본 요금이 주어지고 기본 요금을 초과했을 경우에는 각 케이스의 fees에 따라 요금을 계산해야 된다. 차량은 여러번 입출차할 수 있으나 마지막 출차 기록이 없을 경우 23:59에 출차한 것으로 간주함.

 


전략

 

01. datetime, dict

  시간 계산은 datetime으로 하면 될 것 같고 입차한 차량의 시각을 dict로 저장해뒀다가 출차할 때 해당 차량을 찾아서 시간 차이를 구해주면 될 것 같다. 그리고 여러번 입출차한 기록을 누적해서 더해줄 때도 dict를 이용하면 될 것 같고 마지막으로 차량의 번호가 작은 순으로 정렬해서 산출해주면 되겠다.

 


풀이

 

import datetime
import math

def solution(fees, records):
    parking_stack = {}
    accum = {}
    result = []
    
    # 입출차
    for record in records:
        rec_time, number, io = record.split()
        timestamped_time = datetime.datetime.strptime(rec_time, "%H:%M")
        if io == "IN":
            parking_stack[number] = timestamped_time
        else:
            if accum.get(number):
                accum[number] += timestamped_time - parking_stack[number]
            else:
                accum[number] = timestamped_time - parking_stack[number]
            del parking_stack[number]
    
    # 미출차 강제 출차
    if len(parking_stack):
        for number, t in parking_stack.items():
            if accum.get(number):
                accum[number] += datetime.datetime.strptime("23:59", "%H:%M") - parking_stack[number]
            else:
                accum[number] = datetime.datetime.strptime("23:59", "%H:%M") - parking_stack[number]
    
    # 계산
    for n, t in sorted(accum.items(), key=lambda x: x[0]):
        if fees[0] < t.total_seconds() / 60:
            result.append(fees[1] + math.ceil((t.total_seconds() / 60 - fees[0]) / fees[2]) * fees[3])
        else:
            result.append(fees[1])
    
    return result

 

 의식의 흐름으로 문제를 풀다보니 코드가 지저분하다. 지금 보니 더 간단히 정리할 수 있는 부분이 있다.

 

 먼저 입출차한 차량의 시간을 구하는 파트를 보면 각 기록이 space를 구분자로 가진 문자열로 되어 있기 때문에 split()메소드를 이용해 입출차 시간, 차량 번호, 입/출차 여부로 나눠 주었다. io가 IN일 경우 parking_stack에 해당 차량 번호의 입차 시간을 기록하고, OUT일 경우 입차 시간과의 차이를 구해서 accum 딕셔너리에 넣어주거나 더해주었다. 계산이 끝나고 나면 parking_stack에서 해당 차량 번호를 del 예약어로 지워서 출차됐음을 표현했다.

 

 이제 하루가 지나도 출차한 기록이 없는 차량에 대해 처리해주어야 한다. 문제에서 출차 기록이 없는 경우 23:59에 출차한 것으로 간주하라고 했으니 parking_stack에 아직 차량이 남아있을 경우 추가적으로 23:59에서 입차 기록을 뺀 시간을 다시 accum 딕셔너리에 각각 더해주었다.

 

 마지막으로 요금을 계산하기 위해 accum 딕셔너리를 items() 메소드를 이용해 key, value값을 둘 다 가져와 key를 기준으로 오름차순 정렬하고 기본 시간 이하일 경우와 이상일 경우를 나눠서 각각 계산하고 result 리스트에 차례차례 넣은 뒤 정답을 산출했다.

 

 


다른 사람의 풀이

 

from collections import defaultdict
from math import ceil

class Parking:
    def __init__(self, fees):
        self.fees = fees
        self.in_flag = False
        self.in_time = 0
        self.total = 0

    def update(self, t, inout):
        self.in_flag = True if inout=='IN' else False
        if self.in_flag:  self.in_time = str2int(t)
        else:             self.total  += (str2int(t)-self.in_time)

    def calc_fee(self):
        if self.in_flag: self.update('23:59', 'out')
        add_t = self.total - self.fees[0]
        return self.fees[1] + ceil(add_t/self.fees[2]) * self.fees[3] if add_t >= 0 else self.fees[1]

def str2int(string):
    return int(string[:2])*60 + int(string[3:])

def solution(fees, records):
    recordsDict = defaultdict(lambda:Parking(fees))
    for rcd in records:
        t, car, inout = rcd.split()
        recordsDict[car].update(t, inout)
    return [v.calc_fee() for k, v in sorted(recordsDict.items())]

 

 다른 사람의 풀이를 구경하던 도중 파이썬스러운 풀이가 있어서 가져왔다. 전체적인 흐름은 collections 라이브러리의 defaultdict, lambda, class 세 가지의 조합으로 하나의 딕셔너리에서 key는 차량 번호, value는 Parking 클래스의 인스턴스를 만들어 주고 각각을 계산하도록 한 뒤 마지막으로 calc_fee메소드를 이용해 더해주는 방식이다.

 

 defaultdict를 이런 식으로 활용할 수 있구나 하는 걸 알게 되었다.

반응형