[Code] Study & Practice

[백준문제] 알람 시계(2884번), 좀 더 잘 풀어보기! - 45분 당기기? 부족하다! 알람 늦추기도 해보자.

yssong01 2025. 11. 12. 17:47

이 글에서는 단순히 "45분 당기기"만 처리하는 백준 2884번*의 기본 풀이를 넘어, 어떤 시간 조정(조기상 / 늦잠) 상황에서도 정확하게 동작하는 일반화된 알람 계산 방식을 파이썬으로 구현해 보겠습니다.

☞ 백준문제 2884번 (단계별로 풀어보기 - 조건문 5번: 알람 시계) https://www.acmicpc.net/problem/2884

 

구현을 위한 핵심 아이디어는 하루를 0~1439분의 순환 구조로 보고 모든 시각을 하나의 "분 인덱스"로 다루는 것입니다. 이렇게 관점을 잡으면 시·분 경계, 하루 넘어감(23:59 → 00:00) 같은 문제점이 해결됩니다.


1. 백준 2884번 "알람 시계" 문제의 아쉬운 점

백준 2884번(알람 시계)는 현재 시간에서 45분을 "당겨서" 설정하는 간단한 조건문 문제입니다.

  • 입력: 현재 시간 H M (0 ≤ H ≤ 23, 0 ≤ M ≤ 59)
  • 출력: 45분 "이전" 시간

그래서 많은 풀이가 예제 1, 2, 3만 통과하도록 작성되고, 자연스럽게 "45분 당기기"라는 특수 상황에만 맞춰져 있습니다.

 

예를 들어 아래와 같은 풀이가 보통 답안(solution)입니다.

# 입력 시간 = 알람 셋팅 시간 [hh:mm]
data = input().split()
h = int(data[0])  # 시
m = int(data[1])  # 분

# 일찍 당길 시간 크기 [hh:mm], 당기는 의미를 적용하여 시, 분을 음수로 설정
dh, dm = [0, -45]  # [시:분]

# 셋팅 시간보다 당겨진 시간의 시(h), 분(m)
h = h + dh
m = m + dm

# 출력 시간 계산
if m >= 0:
    pass
elif m < 0:
    if h - 1 >= 0:
        h = h - 1
        m = m + 60
    else:  # h - 1 < 0, 즉 h == 0
        h = h - 1 + 24
        m = m + 60

print(h, m)

"""
# 예제 입력 1
10 10
# 예제 출력 1
9 25

# 예제 입력 2
0 30
# 예제 출력 2
23 45

# 예제 입력 3
23 40
# 예제 출력 3
22 55
"""
 

이 코드는 "45분 빼기(음수 이동)"라는 단일 상황에서는 잘 작동합니다. 하지만 문제의 주어진 입력값에만 부합하는 "틀"을 벗어나서 "시간을 늦춰보기(양수 이동)"를 시도하면, 위 코드 만으로는 정답과 부합하지 않는 한계가 드러납니다.


2. 45분 "늦추기"를 해보면 생기는 문제

 

위 구조를 그대로 가져와서, 이번에는 "45분을 늦춰보기(더하기)"로 바꿔 보겠습니다.

# 입력 시간 = 알람 셋팅 시간 [hh:mm]
data = input().split()
h = int(data[0])  # 시
m = int(data[1])  # 분

# 늦추어질 시간 크기 [hh:mm], 늦추는 의미를 적용하여 시, 분을 양수로 설정
dh, dm = [0, 45]  # [시:분]

h = h + dh
m = m + dm

if m >= 0:
    pass
elif m < 0:
    if h - 1 >= 0:
        h = h - 1
        m = m + 60
    else:
        h = h - 1 + 24
        m = m + 60

print(h, m)

"""
# 예제 입력
23 20
# 실제 출력
23 65  ← 60분을 초과하는 잘못된 분 표시
"""
 
 

여기서 문제가 되는 핵심은 다음과 같습니다.

  • 분이 60 이상이 되면, 시[h]를 1 증가시키고, 분[m]에서 60을 빼야 합니다.
  • 하지만 이 코드는 “분이 음수일 때만” 보정하도록 되어 있기 때문에 분이 60을 넘는 경우는 정상적으로 연산하지 못합니다.

즉, 이 코드의 한계(문제)는 다음과 같습니다.

  • "45분 당기기"라는 백준 2884의 좁은 조건 안에서는 괜찮지만,
  • "알람 시간을 자유롭게 앞/뒤로 조정한다"는 일반적인 관점에서는 불안정한 코드입니다.

이제 이 한계를 극복하기 위해서 일반화된 방식으로 코드를 구현해 보겠습니다.


3. 하루 전체를 "분 인덱스"로 보는 아이디어

시간 계산을 안정적으로 처리하기 위한, 즉 코드를 일반화시키기 위한 아이디어는 다음과 같습니다.

  1. 하루 = 24시간 × 60분 = 1440분
  2. 시, 분 [h, m]을 하나의 정수 인덱스로 변환
    → index = h * 60 + m (0 ≤ index ≤ 1439)
  3. "시간을 당기거나(음수), 늦추는(양수)" 것은
    → index에 음수/양수를 더하는 연산으로 통일
  4. 하루는 ‘순환’이므로
    → "모듈러 연산 % 1440"으로 처리

이것을 수식으로 요약하면:

old_index = h * 60 + m
new_index = (old_index + 이동분량_분) % 1440
 
이렇게 하면,
  • 알람 시간보다 일찍 일어나기 위한 알람 설정 → 음수 이동 (조기상)
  • 알람 시간보다 늦게 일어나기 위한 알람 설정 → 양수 이동 (늦잠)
  • 시·분 경계(59분 → 00분, 23시 → 0시)
  • 하루 넘어감(23:59 → 00:00)

즉, 모든 경우가 같은 규칙, 한 줄짜리 수식으로 해결됩니다.

이 아이디어를 적용하면, 모든 알람 시간 세팅에 부합하는 일반화된 "알람 계산기"를 구현할 수 있습니다.


4. 일반화 코드: 조기상(음수) + 늦잠(양수) 완전 지원

아래 코드는 "알람 초기 시간 + 변경 시간(조기상/늦잠)"을 입력받아서, 변경된 알람 시간을 계산하는 일반화된 코드입니다.

# 알람 시간 일반화 계산기
# - 알람 초기 시간 (h_initial, m_initial)
# - 변경할 시간 크기 (dh, dm)  ← 조기상: 음수 / 늦잠: 양수

def compute_alarm(h_initial, m_initial, dh, dm):
    """알람 시간을 dh시간 dm분 만큼 이동시킨 결과를 반환한다."""
    # 1. 현재 시간을 '하루 기준 분 단위'로 변환 (0 ~ 1439)
    total = h_initial * 60 + m_initial

    # 2. 움직일 총 분량 (음수/양수 모두 허용)
    diff = dh * 60 + dm

    # 3. 하루(24*60분) 기준으로 순환 이동
    new_total = (total + diff) % (24 * 60)

    # 4. 다시 시/분으로 변환
    new_h = new_total // 60
    new_m = new_total % 60
    return new_h, new_m


def main():
    # 초기 알람 시간 입력
    h_initial, m_initial = map(int, input('알람 설정 시간 [hh mm] = ').split())

    # 변경할 시간 크기 입력 (음수: 조기상, 양수: 늦잠)
    dh, dm = map(int, input('변경할 시간 크기 [hh mm] = ').split())

    # 알람 계산
    set_hour, set_mins = compute_alarm(h_initial, m_initial, dh, dm)

    # 결과 출력
    print()
    print(f'셋팅한 초기 알람 시간 = {h_initial} {m_initial}')
    print(f'움직인 시간 [hh:mm] = {dh} {dm}')
    print(f'변경된 알람 시간 = {set_hour} {set_mins}')


if __name__ == "__main__":
    main()
 

예시 1: 늦잠(양수 이동)

알람 설정 시간 [hh mm] = 23 50
변경할 시간 크기 [hh mm] = 1 20

셋팅한 초기 알람 시간 = 23 50
움직인 시간 [hh:mm] = 1 20
변경된 알람 시간 = 1 10
  • 23:50에서 1시간 20분 늦잠 → 01:10으로 정확히 계산됩니다.

예시 2: 조기상(음수 이동)

알람 설정 시간 [hh mm] = 0 45
변경할 시간 크기 [hh mm] = -1 -25

셋팅한 초기 알람 시간 = 0 45
움직인 시간 [hh:mm] = -1 -25
변경된 알람 시간 = 23 20
  • 00:45에서 1시간 25분 당겨서(조기상) → 전날 23:20으로 정확히 연산됩니다.
  • 하루 24시가 "순환"하도록 % (24*60)으로 처리했기 때문에 가능한 결과입니다.

5. 핵심 포인트 한 번 더 짚어보기

5-1. 시·분 단위 통일

  • dh: 시간 단위 이동량
  • dm: 분 단위 이동량

시와 분을 "분" 단위 하나로 통합하면 (1시간 → 60분), 수식이 단순해집니다. 

diff = dh * 60 + dm
  • dh = -1, dm = -45 → diff = -60 - 45 = -105분
  • dh = 1, dm = 20 → diff = 60 + 20 = 80분

시·분이라는 2차원 개념을 "한 줄짜리 선(분)" 위에서의 이동량으로 바꾼 것입니다.


5-2. 하루를 0~1439분으로 바꾸기

이제 모든 시각을 0~1439 사이의 정수로 다룸으로써, 시간 계산이 단순한 정수 덧셈 문제로 바뀝니다.

total = h_initial * 60 + m_initial
  • 00:00 → 0분
  • 00:01 → 1분
  • 01:00 → 60분
  • 23:59 → 23*60 + 59 = 1439분

5-3. 하루 순환 처리

  • (total + diff)만 사용하면 음수가 될 수도 있고 1440을 넘어서 리스트 범위를 벗어날 수도 있습니다.
  • % (24*60)을 붙여 줌으로써 항상 0 ≤ new_total ≤ 1439 범위 안으로 “순환”시킵니다.
new_total = (total + diff) % (24 * 60)
  • total = 0, diff = -1 → (0 - 1) % 1440 = 1439 → 23:59
  • total = 1435, diff = 10 → (1435 + 10) % 1440 = 1445 % 1440 = 5 → 00:05

이렇게 한 줄로 구성하면, 전날 또는 다음날로 넘어가는 상황까지 모두 해결됩니다.


5-4. 다시 시·분으로 되돌리기

new_h = new_total // 60
new_m = new_total % 60
  • 정수 나눗셈(//)으로 시(hour), 나머지(%) 연산으로 분(minute)을 구합니다.
  • 이렇게 하여, “백준 2884의 45분 당기기”는 물론이고, “임의의 시간/분만큼 앞뒤로 움직이기”라는 일반적인 알람 계산이 모두 안정적으로 처리됩니다.

6. 백준 2884번에 다시 적용해 보기 (단순화 버전)

위에서 살펴본 일반화 아이디어를 그대로 적용하면, 백준 제출용 코드는 오히려 더 짧고 간단해집니다.

H, M = map(int, input().split())

# 1. 현재 시각을 '전체 분'으로 변환
total = H * 60 + M

# 2. 45분 당기기 → -45분 이동
total = (total - 45) % (24 * 60)

# 3. 다시 시/분으로 변환
print(total // 60, total % 60)
  • 조건문 없이, 시/분 경계 고민 없이, 경계값(0시, 0분 등)까지 깔끔하게 처리할 수 있습니다.

이 코드는 "하루 전체를 분으로 보고, 모듈러 연산으로 순환하는" 앞에서 설명한 일반화 아이디어를 간결하게 구현했습니다.


7. 글을 맺으며

  • 단순히 조건문으로 예제만 해결하는 방식을 넘어서, 하루 전체를 0~1439분의 순환 구조로 바라보면 시간 조정이 훨씬 간결하고 안정적으로 처리됩니다.
  • 이렇게 시·분의 경계를 고민하지 않고도 **조기상(음수 이동)**과 **늦잠(양수 이동)**을 모두 하나의 규칙으로 계산할 수 있다는 점이 핵심입니다.
  • 백준 2884번은 이러한 시간·모듈러 방식을 연습하기 좋은 사례이며, 실제 알람 앱이나 스케줄러 기능을 설계할 때도 그대로 적용할 수 있는 실용적인 개념을 연습하기에도 좋은 문제입니다.

 

즐거운 파이썬, 즐 파이씽~

 

[주의] 내용에 오류가 있을 수 있습니다.