알고리즘 스터디

[백준/파이썬][Gold IV] PC방 요금 - 9080

난쟁이 개발자 2025. 2. 11. 00:05
반응형

[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)
  • spd > 300 조건 : 
    • 일반 요금은 1시간(60분)마다 1000원인데, 5시간(300분) 동안이면 5000원이 된다.
      즉, 남은 시간이 300분을 초과한다면 일반 요금으로 계산하면 5000원 이상이 나오게 되므로
      야간 정액(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원 이상이 될 것"이라는 합리적인 판단에 기반
  • 시간 시뮬레이션의 단순화 
    • 남은 시간을 60분씩 줄이면서 계산하다가, 야간 구간에 진입하면 한 번에 야간 정액이 적용되는 시간을 계산해 빼므로, 복잡한 분 단위의 요금 계산 문제를 단순하게 "한 시간씩 또는 한 구간씩" 진행하는 방식으로 해결할 수 있음.

 

 

 

반응형