[Gold IV] PC방 요금 - 9080
성능 요약
메모리: 108384 KB, 시간: 88 ms
분류
구현, 시뮬레이션
제출 일자
2025년 2월 11일 00:04:02
문제 설명
현성이는 요즘 LINEAR 2라는 온라인 게임에 빠져있다. PC방에 가서 게임을 즐기는데, 자주 가는 PC방의 요금체계는 다음과 같다. 일반 요금으로 시간당 1000원 씩을 받으며, 야간 정액을 끊으면 5000원만 내고 밤 10시부터 다음날 아침 8시까지 사용할 수 있다. 이 PC방에서는 1시간에서 1분이라도 넘으면 새로운 1시간에 대한 요금을 부과한다. 그리고 이미 일반 요금으로 사용을 하다가 야간 정액을 쓰게 되면 일반 요금을 미리 계산을 하고 야간 정액을 쓸 수 있다. 즉, 일반 요금을 쓰다가 야간 정액을 쓰고 다시 일반 요금을 쓰게 되면 두 개의 일반 요금을 각각 따로 계산이 된다.
현성이가 게임을 시작하는 시각과 게임을 하는 시간을 입력으로 받으면 현성이가 최소로 지불해야 하는 PC방 이용 요금을 계산하는 프로그램을 작성하시오. 현성이는 한 번 게임을 시작하면 게임을 하는 시간 동안 계속 게임을 한다고 하자.
입력
입력의 첫 줄에는 테스트 케이스의 개수 T (1 ≤ T ≤ 10)가 주어진다. 각 테스트 케이스는 한 줄에 HH:MM D가 주어지며 HH와 MM사이에는 :(콜론)이 있고, MM과 D사이에는 한 칸의 공백이 주어진다. HH:MM은 현성이가 게임을 시작하는 시각을 나타내며 HH시 MM분부터 시작함을 의미한다. D는 현성이가 게임을 하는 시간을 분으로 나타내며 정수로 주어진다. HH:MM은 00:00부터 23:59까지의 시각으로 표시되며 모두 2자리로 표시된다(숫자가 한 자리일 때에는 앞에 0이 붙어서 주어진다.). 사람 체력의 한계상 D는 3일 동안의 시간까지만 입력된다. (1 ≤ D ≤ 4320)
출력
각 테스트 케이스에 대해서 현성이가 지불해야 하는 최소의 PC방 이용 요금을 한 줄에 하나씩 출력한다(단위 원은 생략).
풀이
def main() :
hm, spd = input().split()
hh, mm = map(int, hm.split(':'))
spd = int(spd)
ans = 0
hh = (hh + 2) % 24 # 밤 10시 - 아침 8시 에서 00시 - 10시로 시간 이동시킴
while spd > 0 :
# 야간 정액 구간이 00:00 - 10:00
# 이때 야간 정액을 쓰기 유리한 시점이 00:00 ~ 04:00에 해당되는 조건
# spd > 300 : 5시간 이상 사용을 해야 이득임.
if hh <= 4 and spd > 300 :
spd -= (600 - (hh * 60 + mm))
hh = 10
mm = 0
ans += 5000
else :
hh = (hh + 1) % 24 # 1시간 이용
spd -= 60 # 1시간 이용
ans += 1000 # 요금 지불
print(ans)
T = int(input())
for _ in range(T) :
main()
처음에 이걸 풀이할때 22시와 8시 사이에서 많이 헤매어서 결국 코드를 참고 하였다.
처음에 이 코드를 접하고 나서 뭔 소린가 싶어서 이래저래 알아본 결과, 예전에 비슷한걸 풀어본 경험이 있었는데 얕게 공부한 것이 치명적인 단점이 되었다.
22시 ~ 8시는 중간에 24시가 되어 0시로 초기화가 되기 때문에 이 과정이 생각보다 많이 번거롭다. 그래서 22시에 2 를 더하여 24로 맞춰준 뒤 0으로 바꿔버리면,
22시 ~ 8시 였던 야간 시간이 0시 ~ 10시로 바뀌어 버리게 된다. 이 뒤 부터는 편하게 계산하면 된다.
야간 요금을 이용하는 경우는 이용 시간이 5시간 이상 이여야 이득이기 때문에 이용 시작 시간이 00:00 ~ 04:00 사이여야 한다. 그리고 총 이용 시간이 5시간 이상이어야 한다.
처음에는 굉장히 어렵다고 생각했었는데 이렇게 생각을 하고 나니까 생각보다 별 거 아닌 문제였다고 생각한다. 새로운 시각에서 보는 습관을 들여야 겠다.
1. 시간 이동(shift)로 야간 구간을 쉽게 처리
- 원래 문제에는 야간 정액은 밤 10시 (22:00)부터 다음날 아침 8시(08:00)까지 사용할 수 있다.
- 그런데 이 구간은 자정을 넘어가므로 계산하기 까다로움.
- 그래서 코드에서는
- 이 결과, 원래 야간 구간 (22:00 ~ 08:00)은 이동 후 00:00 ~ 10:00 구간이 된다.
h, m = map(int, time.split(":"))
h = (h + 2) % 24
- 예를 들어, 원래 22:00은 (22+2)%24 = 00:00
08:00은 (8+2)%24 = 10:00이 되어,
계산하기 쉬운 연속구간이 된다.
2. 그리디하게 시간 진행 시뮬레이션
- 남은 게임 시간(분 단위)을 spd에 저장하고, 전체 비용을 ans에 누적
- while 루프를 돌면서 남은 시간 spd가 0보다 클 떄까지 다음 두 방식 중 하나를 선택한다.
(1) 야간 정액 사용 조건
if h <= 4 and spd > 300:
- h <= 4 조건 :
- 이동된 시간으로 보면, 야간 정액 사용 가능 구간은 00:00 ~ 10:00인데,
그 중에서도 "야간 정액"을 쓰기에 유리한 시점은 초반부, 즉 원래 22:00 ~ 02:00에 해당하는 구간
(이동 후 00:00 ~ 04:00)
- 이동된 시간으로 보면, 야간 정액 사용 가능 구간은 00:00 ~ 10:00인데,
- spd > 300 조건 :
- 일반 요금은 1시간(60분)마다 1000원인데, 5시간(300분) 동안이면 5000원이 된다.
즉, 남은 시간이 300분을 초과한다면 일반 요금으로 계산하면 5000원 이상이 나오게 되므로
야간 정액(5000원)을 쓰는 것이 이득입니다.
- 일반 요금은 1시간(60분)마다 1000원인데, 5시간(300분) 동안이면 5000원이 된다.
- 만약 두 조건이 모두 만족되면,
spd -= (600 - (h * 60 + m))
h = 10
m = 0
ans += 5000
- (600 - (h * 60 + m)) : 현재 이동된 시간에서 10:00(600분)까지 남은 분 수를 계산
- 즉, 야간 정액은 남은 시간 중 10:00 까지의 사용을 커버하므로, 그 시간만큼 spd에서 빼고, 시간을 10:00으로 맞춤.
- 비용은 5000원을 추가
(2) 일반 요금 처리
else:
h = (h + 1) % 24
spd -= 60
ans += 1000
- 위 조건을 만족하지 않는 경우. 1시간 단위로 일반 요금을 적용
3. 왜 이 방식이 가능한가?
- 시간 이동의 효과
- 문제의 야간 정액 구간 22:00 - 08:00을 이동 후 00:00 - 10:00으로 만들면 자정을 넘는 복잡한 처리를 단순한 연속 구간으로 계산할 수 있게 됨.
- 그리디한 선택
- 코드에서는 현재 시간이 이동 후 04:00이고 남은 시간이 300분 이상이면 무조건 야간 정액을 쓰도록 합니다.
이는 "남은 시간이 5시간을 넘으면 일반 요금으로는 5000원 이상이 될 것"이라는 합리적인 판단에 기반
- 코드에서는 현재 시간이 이동 후 04:00이고 남은 시간이 300분 이상이면 무조건 야간 정액을 쓰도록 합니다.
- 시간 시뮬레이션의 단순화
- 남은 시간을 60분씩 줄이면서 계산하다가, 야간 구간에 진입하면 한 번에 야간 정액이 적용되는 시간을 계산해 빼므로, 복잡한 분 단위의 요금 계산 문제를 단순하게 "한 시간씩 또는 한 구간씩" 진행하는 방식으로 해결할 수 있음.
'알고리즘 스터디' 카테고리의 다른 글
[백준/파이썬][Bronze I] The Easiest Problem is This One - 6627 (0) | 2025.02.12 |
---|---|
[백준/파이썬][Bronze II] 번호표 교환 - 11949 (0) | 2025.02.12 |
[백준/파이썬][Gold IV] 떡 돌리기 - 20007 (1) | 2025.02.09 |
[백준/파이썬][Gold V] 개업 - 13910 (0) | 2025.02.08 |
[백준/파이썬][Silver III] Champernowne Count - 27569 (1) | 2025.02.07 |