문제
https://school.programmers.co.kr/learn/courses/30/lessons/92341
주차장의 요금표와 차량 입출차 기록이 주어졌을 때 차량 별로 주차 요금을 계산하는 문제
기본 요금이 주어지고 기본 요금을 초과했을 경우에는 각 케이스의 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를 이런 식으로 활용할 수 있구나 하는 걸 알게 되었다.
'프로그래밍 > 문제풀이' 카테고리의 다른 글
[코딩테스트] 해시 - 전화번호 목록 (0) | 2023.07.28 |
---|---|
[코딩테스트] 힙(Heap) - 더 맵게 (0) | 2023.07.25 |
[코딩테스트] 탐욕법(Greedy) - 단속카메라 (0) | 2023.07.20 |
[코딩테스트] 2019 KAKAO BLIND RECRUITMENT - 실패율 (0) | 2023.07.19 |
[코딩테스트] 2018 KAKAO BLIND RECRUITMENT - 캐시 (0) | 2023.07.19 |