문제 링크
코드트리 | 코딩테스트 준비를 위한 알고리즘 정석
국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.
www.codetree.ai
풀이
해설에서 아이디어를 참고해서 풀었고, heapq를 사용했다.
각 명령을 실시간으로 처리하는 것이 아니라, 모든 명령들에 대해서 필요한 값들을 미리 저장해두는 작업이 필요하다.
필요한 변수는 아래와 같다.
l, q = map(int,input().split())
# 전체 쿼리 저장
query = []
# 사람 별 도착 시간 저장
entry_time = {}
# 사람 별 위치 저장
position = {}
# 사람 별 떠나는 시간 저장
exit_time = defaultdict(int)
# 사람 이름 저장
names = set()
# 사람 별 쿼리 저장
p_queries = defaultdict(list)
1. 명령어 저장
heapq를 사용하여, 각 명령을 들어온 시간, 명령 정보 순으로 저장한다.
들어온 시간이 같다면,
초밥 만들기 -> 초밥 먹기 -> 새로운 손님 입장하기 -> 손님 떠나기 순으로 처리하고 값을 출력하기 위해서이다.
따라서 각 명령에 대해 query에 필요한 값을 저장하고,
초밥 만들기 명령이라면, 손님 별 쿼리에도 추가한다.
손님 입장하기 명령이라면, 손님 이름을 추가하고 입장 시간과 입장 위치도 저장한다.
for _ in range(q):
data = list(map(str,input().split()))
q_type = int(data[0])
t, x, n = -1 ,-1 ,-1
name = ""
# 스시 들어옴
if q_type == 100 :
# 들어온 시간, 위치, 사람 이름
t, x, name = data[1:]
t = int(t)
x = int(x)
if q_type == 200 :
# 들어온 시간, 위치, 사람 이름, 초밥 개수
t, x, name, n = data[1:]
t = int(t)
x = int(x)
n = int(n)
if q_type == 300 :
# 사진 촬영
t = int(data[1])
# t와 cmd 순서대로
# t가 같다면 초밥 만들고 -> 즉시 초밥 사라지고 -> 새로운 손님 입장하고 -> 손님 사라지고 -> 사진 촬영 순으로
heapq.heappush(query, (t, q_type, x, name, n))
if q_type == 100 :
p_queries[name].append((t, q_type, x, name,n))
if q_type == 200 :
names.add(name)
entry_time[name] = t
position[name] = x
2. 각 손님 별 떠나는 시간 구하기
아이디어는 다음과 같다.
1) 초밥이 손님보다 먼저 만들어 진 경우
손님이 입장한 시간 때의 초밥 위치를 구해서 손님과 만나기 위해서는 몇 초간 더 이동해야 하는지 구해,
최종적으로 만나는 시간을 구한다.
손님이 입장한 시간 때의 초밥 위치는 (현재 위치 + (초밥 만들어진 시간 - 손님 입장 시간)) % 의자 개수 이다.
최종적으로 만나는 시간은 아래 코드와 같다.
# 초밥과 손님이 만나는 시간
match_time = 0
# 초밥이 사람이 오기 전에 주어짐
if t < entry_time[name]:
t_diff = entry_time[name] - t
# 사람이 들어왔을 때 스시 위치
s_pos = (x + t_diff) % l
match_time = entry_time[name]
if position[name] > s_pos :
add_time = position[name] - s_pos
match_time += add_time
elif s_pos > position[name]:
add_time = l - (s_pos - position[name])
match_time += add_time
# 몇 초가 더 지나야 만나는지
add_time = (position[name] - s_pos + l) % l
match_time = entry_time[name] + add_time
2) 초밥이 손님이 입장한 이후에 만들어진 경우
초밥이 만들어진 위치에서 손님까지가기 위해 몇 초간 더 이동해야 하는지 구해,
최종적으로 만나는 시간을 구한다.
match_time = t
if position[name] > x :
add_time = position[name] - x
match_time += add_time
elif x > position[name]:
add_time = l - (x - position[name])
match_time += add_time
손님 별 초밥을 먹는 가장 늦은 시간을 갱신하고, 초밥이 사라지는 명령을 추가한다.
여기서 같은 시간일 경우 초밥이 만들어지고 나서 초밥이 사라지기 때문에 명령어는 100과 200 사이인 111 로 설정
for name in names :
for t, cmd, x, p_n, n in p_queries[name]:
#print(t, cmd, x, p_n, n, entry_time[name], position[name])
# 초밥과 손님이 만나는 시간
match_time = 0
# 초밥이 사람이 오기 전에 주어짐
if t < entry_time[name]:
t_diff = entry_time[name] - t
# 사람이 들어왔을 때 스시 위치
s_pos = (x + t_diff) % l
match_time = entry_time[name]
if position[name] > s_pos :
add_time = position[name] - s_pos
match_time += add_time
elif s_pos > position[name]:
add_time = l - (s_pos - position[name])
match_time += add_time
# 몇 초가 더 지나야 만나는지
add_time = (position[name] - s_pos + l) % l
match_time = entry_time[name] + add_time
else :
add_time = (position[name] - x + l) % l
match_time = t + add_time
# 초밥 먹는 가장 늦은 시간 갱신
exit_time[name] = max(exit_time[name], match_time)
# 초밥 사라지는 쿼리 추가
heapq.heappush(query, (match_time, 111, -1, name, -1))
3. 손님 떠나는 명령 추가
손님 별 초밥을 먹는 가장 늦은 시간이 손님이 떠나는 시간이므로 2번에서 구한 값을 바탕으로
시간 별 손님이 떠나는 명령어를 추가한다.
해당 명령은 200와 300사이의 222로 설정한다.
for name in names:
heapq.heappush(query, (exit_time[name], 222, -1, name, -1))
4. 시점 별 초밥 수, 손님 수 계산
p_num = 0
s_num = 0
while query:
data = heapq.heappop(query)
cmd = data[1]
if cmd == 100:
s_num += 1
elif cmd == 111 :
s_num -=1
elif cmd == 200:
p_num += 1
elif cmd == 222 :
p_num -= 1
else :
print(p_num, s_num)
전체 코드는 아래와 같다.
#-*- coding: utf-8 -*-
import heapq
from collections import defaultdict
l, q = map(int,input().split())
# 전체 쿼리 저장
query = []
# 사람 별 도착 시간 저장
entry_time = {}
# 사람 별 위치 저장
position = {}
# 사람 별 떠나는 시간 저장
exit_time = defaultdict(int)
# 사람 이름 저장
names = set()
# 사람 별 쿼리 저장
p_queries = defaultdict(list)
for _ in range(q):
data = list(map(str,input().split()))
q_type = int(data[0])
t, x, n = -1 ,-1 ,-1
name = ""
# 스시 들어옴
if q_type == 100 :
# 들어온 시간, 위치, 사람 이름
t, x, name = data[1:]
t = int(t)
x = int(x)
if q_type == 200 :
# 들어온 시간, 위치, 사람 이름, 초밥 개수
t, x, name, n = data[1:]
t = int(t)
x = int(x)
n = int(n)
if q_type == 300 :
# 사진 촬영
t = int(data[1])
# t와 cmd 순서대로
# t가 같다면 초밥 만들고 -> 즉시 초밥 사라지고 -> 새로운 손님 입장하고 -> 손님 사라지고 -> 사진 촬영 순으로
heapq.heappush(query, (t, q_type, x, name, n))
if q_type == 100 :
p_queries[name].append((t, q_type, x, name,n))
if q_type == 200 :
names.add(name)
entry_time[name] = t
position[name] = x
for name in names :
for t, cmd, x, p_n, n in p_queries[name]:
#print(t, cmd, x, p_n, n, entry_time[name], position[name])
# 초밥과 손님이 만나는 시간
match_time = 0
# 초밥이 사람이 오기 전에 주어짐
if t < entry_time[name]:
t_diff = entry_time[name] - t
# 사람이 들어왔을 때 스시 위치
s_pos = (x + t_diff) % l
match_time = entry_time[name]
if position[name] > s_pos :
add_time = position[name] - s_pos
match_time += add_time
elif s_pos > position[name]:
add_time = l - (s_pos - position[name])
match_time += add_time
else :
match_time = t
if position[name] > x :
add_time = position[name] - x
match_time += add_time
elif x > position[name]:
add_time = l - (x - position[name])
match_time += add_time
# 초밥 먹는 가장 늦은 시간 갱신
exit_time[name] = max(exit_time[name], match_time)
# 초밥 사라지는 쿼리 추가
heapq.heappush(query, (match_time, 111, -1, name, -1))
# 사람 떠나는 쿼리 추가
for name in names:
heapq.heappush(query, (exit_time[name], 222, -1, name, -1))
p_num = 0
s_num = 0
while query:
data = heapq.heappop(query)
cmd = data[1]
if cmd == 100:
s_num += 1
elif cmd == 111 :
s_num -=1
elif cmd == 200:
p_num += 1
elif cmd == 222 :
p_num -= 1
else :
print(p_num, s_num)
'개발 > algorithm' 카테고리의 다른 글
[코드트리] 루돌프의 반란 - Python (1) | 2023.10.16 |
---|---|
[코드트리] 코드트리 채점기 - Python (1) | 2023.10.13 |
[코드트리] 토끼와 경주 - Python (0) | 2023.10.13 |
[코드트리] 메이즈 러너 - Python (0) | 2023.10.12 |
[코드트리] 산타의 선물 공장2 - Python (1) | 2023.10.11 |