{ }codelper
음수 나머지덧셈P0166

세계 여러 도시의 회의 시각

세계 여러 도시의 사람들이 한 화상 회의에 들어온다. 첫 줄에 회의를 시작하는 협정 세계시(UTC) 시각 HH:MM과 회의 길이 D(분)가 빈칸으로 나뉘어 주어진다. 둘째 줄에 도시 수 n이 주어지고, 다음 n줄에 도시 이름과 그 도시의 시차가 빈칸으로 나뉘어 하나씩 주어진다. 시차는 "+HH:MM" 또는 "-HH:MM" 꼴이고, 그 도시의 시각은 UTC 시각에 시차를 더한 것이다. 부호는 시와 분 모두에 붙는다.
예를 들어 "-03:30"은 3시간 30분을 빼라는 뜻이다. 시차가 0이면 "+00:00"으로 쓴다. 도시마다 회의를 시작하는 그 도시의 시각과 끝나는 그 도시의 시각을 구하라. 끝나는 시각은 시작하는 시각에 D분을 더한 것이다. 시각마다 날짜 차이도 함께 구한다.
날짜 차이는 그 도시의 날짜가 회의를 시작하는 순간의 UTC 날짜보다 며칠 뒤인지를 나타낸 정수이고, 하루 전이면 -1이다. 도시마다 한 줄에 이름, 시작 시각, 시작 날짜 차이, 끝 시각, 끝 날짜 차이를 빈칸 하나씩 띄워 입력 차례대로 출력하라. 시각은 HH:MM(시와 분 모두 두 자리, 00:00 ~ 23:59)으로 쓴다. 날짜 차이는 0보다 크면 앞에 "+"를 붙이고(+1), 0이면 0, 0보다 작으면 -1처럼 쓴다. 마지막 줄에 시작 날짜 차이와 끝 날짜 차이가 다른 도시의 수, 곧 회의 중에 그 도시에서 자정을 지나는 도시의 수를 출력하라.

입력
00:00 ≤ 시작 시각 ≤ 23:59, 1 ≤ D ≤ 1,440, 1 ≤ n ≤ 1,000, 도시 이름은 영어 글자 1~20개, -12:00 ≤ 시차 ≤ +14:00이고 시차의 분은 00·15·30·45 중 하나
출력
도시마다 한 줄(이름, 시작 시각, 날짜 차이, 끝 시각, 날짜 차이), 마지막 줄에 자정을 지나는 도시의 수
예시 1 · stdin
01:10 120
5
Seoul +09:00
Noronha -02:00
StJohns -03:30
Kathmandu +05:45
Kiritimati +14:00
stdout
Seoul 10:10 0 12:10 0
Noronha 23:10 -1 01:10 0
StJohns 21:40 -1 23:40 -1
Kathmandu 06:55 0 08:55 0
Kiritimati 15:10 0 17:10 0
1
예시 2 · stdin
23:30 1440
3
London +00:00
Baker -12:00
Auckland +13:00
stdout
London 23:30 0 23:30 +1
Baker 11:30 0 11:30 +1
Auckland 12:30 +1 12:30 +2
3
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

음수 시각을 하루 안으로 접는 것이 이 문제의 함정이에요.
Noronha에서는 UTC 01:10에 시차 -02:00을 더해 -50분이 되는데, 이것은 전날 23:10이에요.
Python의 %와 //는 내림 나눗셈이라 divmod(-50, 1440)이 (-1, 1390)으로 바로 맞아요.
C·C++·Java·C#·JS·TS의 %는 0 쪽으로 버린 나눗셈의 나머지라 -50 % 1440이 -50이에요(크레이콘의 %도 JS와 같아요).
그래서 (t % 1440 + 1440) % 1440처럼 1440을 더해 한 번 더 나누거나, Java는 Math.floorMod(t, 1440)을 써요.
날짜 차이도 같은 함정이에요.
C·C++·Java·C#의 -50 / 1440은 0이라 전날이라는 정보가 사라져요(JS·TS·크레이콘의 /는 -0.0347…을 주니 Math.floor가 필요해요).
Python //, Java Math.floorDiv는 -1을 주고, 다른 언어는 (t - 그날의 분) / 1440처럼 딱 나누어떨어지게 만든 뒤 나누면 돼요.
셋째 함정은 시차를 읽는 법이에요.
"-03:30"을 시 -3과 분 +30으로 따로 읽어 더하면 -150분이 되지만, 부호는 둘 다에 붙어서 -210분이에요.
"-00:45"처럼 시가 0이면 시를 정수로 읽은 부호(-0)가 사라지니, 부호 글자를 먼저 떼어 따로 곱하는 것이 안전해요.
두 자리 채우기는 Python f"{m:02d}", JS·TS padStart(2, "0"), Java String.format("%02d"), C# {m:D2}, C printf("%02d"), C++ std::setw(2)와 std::setfill('0'), 크레이콘 앞채우기(2, "0")예요.

Pythonmain.py · 27 줄
def offset_minutes(s):    # "-03:30"의 부호는 시와 분 모두에 붙는다: -(3 * 60 + 30) = -210    sign = -1 if s[0] == "-" else 1    return sign * (int(s[1:3]) * 60 + int(s[4:6]))  def local(t):    # divmod는 내림 나눗셈이라 t가 음수여도 나머지가 0~1439, 몫이 날짜 차이(-1 등)다    day, minute = divmod(t, 1440)    tag = "+" + str(day) if day > 0 else str(day)    return f"{minute // 60:02d}:{minute % 60:02d} {tag}", day  hhmm, d = input().split()start = int(hhmm[:2]) * 60 + int(hhmm[3:])d = int(d)n = int(input())crossing = 0for _ in range(n):    name, off = input().split()    t = start + offset_minutes(off)    begin, day1 = local(t)    end, day2 = local(t + d)    print(name, begin, end)    if day1 != day2:        crossing += 1print(crossing)
JavaScriptmain.js · 27 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const DAY = 1440;// "-03:30"의 부호는 시와 분 모두에 붙는다: -(3 * 60 + 30) = -210const offsetMinutes = (s) => (s[0] === "-" ? -1 : 1) * (Number(s.slice(1, 3)) * 60 + Number(s.slice(4, 6)));const two = (v) => String(v).padStart(2, "0");const local = (t) => {  // JS의 %는 음수를 그대로 남긴다(-50 % 1440 === -50) → 한 번 더해서 다시 나눈다  const minute = ((t % DAY) + DAY) % DAY;  const day = (t - minute) / DAY; // 이제 딱 나누어떨어진다(내림과 같다)  return { text: `${two(Math.floor(minute / 60))}:${two(minute % 60)} ${day > 0 ? "+" + day : day}`, day };};const [hhmm, dText] = lines[0].trim().split(/\s+/);const start = Number(hhmm.slice(0, 2)) * 60 + Number(hhmm.slice(3));const d = Number(dText);const n = Number(lines[1]);const out = [];let crossing = 0;for (let i = 0; i < n; i++) {  const [name, off] = lines[2 + i].trim().split(/\s+/);  const t = start + offsetMinutes(off);  const begin = local(t);  const end = local(t + d);  out.push(`${name} ${begin.text} ${end.text}`);  if (begin.day !== end.day) crossing++;}out.push(String(crossing));console.log(out.join("\n"));
TypeScriptmain.ts · 28 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const DAY = 1440;// "-03:30"의 부호는 시와 분 모두에 붙는다: -(3 * 60 + 30) = -210const offsetMinutes = (s: string): number => (s[0] === "-" ? -1 : 1) * (Number(s.slice(1, 3)) * 60 + Number(s.slice(4, 6)));const two = (v: number): string => String(v).padStart(2, "0");const local = (t: number): { text: string; day: number } => {  // JS의 %는 음수를 그대로 남긴다(-50 % 1440 === -50) → 한 번 더해서 다시 나눈다  const minute = ((t % DAY) + DAY) % DAY;  const day = (t - minute) / DAY; // 이제 딱 나누어떨어진다(내림과 같다)  return { text: `${two(Math.floor(minute / 60))}:${two(minute % 60)} ${day > 0 ? "+" + day : day}`, day };};const [hhmm, dText] = lines[0].trim().split(/\s+/);const start = Number(hhmm.slice(0, 2)) * 60 + Number(hhmm.slice(3));const d = Number(dText);const n = Number(lines[1]);const out: string[] = [];let crossing = 0;for (let i = 0; i < n; i++) {  const [name, off] = lines[2 + i].trim().split(/\s+/);  const t = start + offsetMinutes(off);  const begin = local(t);  const end = local(t + d);  out.push(`${name} ${begin.text} ${end.text}`);  if (begin.day !== end.day) crossing++;}out.push(String(crossing));console.log(out.join("\n"));
JavaMain.java · 37 줄
import java.io.BufferedReader;import java.io.InputStreamReader; public class Main {    static final int DAY = 1440;     // "-03:30"의 부호는 시와 분 모두에 붙는다: -(3 * 60 + 30) = -210    static int offsetMinutes(String s) {        int sign = s.charAt(0) == '-' ? -1 : 1;        return sign * (Integer.parseInt(s.substring(1, 3)) * 60 + Integer.parseInt(s.substring(4, 6)));    }     static String local(int t) {        // Java의 %는 음수를 남기고 /는 0 쪽으로 버린다 → floorMod, floorDiv를 쓴다        int minute = Math.floorMod(t, DAY);        int day = Math.floorDiv(t, DAY);        return String.format("%02d:%02d %s%d", minute / 60, minute % 60, day > 0 ? "+" : "", day);    }     public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] first = in.readLine().trim().split("\\s+");        int start = Integer.parseInt(first[0].substring(0, 2)) * 60 + Integer.parseInt(first[0].substring(3));        int d = Integer.parseInt(first[1]);        int n = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        int crossing = 0;        for (int i = 0; i < n; i++) {            String[] p = in.readLine().trim().split("\\s+");            int t = start + offsetMinutes(p[1]);            out.append(p[0]).append(' ').append(local(t)).append(' ').append(local(t + d)).append('\n');            if (Math.floorDiv(t, DAY) != Math.floorDiv(t + d, DAY)) crossing++;        }        out.append(crossing).append('\n');        System.out.print(out);    }}
C#Main.cs · 44 줄
using System;using System.Text; class Program{    const int Day = 1440;     // "-03:30"의 부호는 시와 분 모두에 붙는다: -(3 * 60 + 30) = -210    static int OffsetMinutes(string s)    {        int sign = s[0] == '-' ? -1 : 1;        return sign * (int.Parse(s.Substring(1, 2)) * 60 + int.Parse(s.Substring(4, 2)));    }     // C#의 %는 나뉘는 수의 부호를 따른다(-50 % 1440 == -50) → 더해서 다시 나눈다    static int MinuteOfDay(int t) => (t % Day + Day) % Day;    // 날짜 차이는 내림: t - (그날의 분)은 1440으로 딱 나누어떨어진다    static int DayShift(int t) => (t - MinuteOfDay(t)) / Day;     static string Local(int t)    {        int m = MinuteOfDay(t), day = DayShift(t);        return $"{m / 60:D2}:{m % 60:D2} {(day > 0 ? "+" : "")}{day}";    }     static void Main()    {        string[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int start = int.Parse(first[0].Substring(0, 2)) * 60 + int.Parse(first[0].Substring(3));        int d = int.Parse(first[1]);        int n = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        int crossing = 0;        for (int i = 0; i < n; i++)        {            string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            int t = start + OffsetMinutes(p[1]);            sb.Append(p[0]).Append(' ').Append(Local(t)).Append(' ').Append(Local(t + d)).Append('\n');            if (DayShift(t) != DayShift(t + d)) crossing++;        }        sb.Append(crossing).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 36 줄
#include <iomanip>#include <iostream>#include <sstream>#include <string> constexpr int DAY = 1440; // C++의 %도 0 쪽으로 버린 나눗셈의 나머지(-50 % 1440 == -50) → 더해서 다시 나눈다int minuteOfDay(int t) { return (t % DAY + DAY) % DAY; }// 날짜 차이는 내림: t - (그날의 분)은 1440으로 딱 나누어떨어진다int dayShift(int t) { return (t - minuteOfDay(t)) / DAY; } std::string local(int t) {    int m = minuteOfDay(t), day = dayShift(t);    std::ostringstream s;    s << std::setfill('0') << std::setw(2) << m / 60 << ':' << std::setw(2) << m % 60 << ' '      << (day > 0 ? "+" : "") << day;    return s.str();} int main() {    std::string hhmm;    int d, n;    std::cin >> hhmm >> d >> n;    int start = std::stoi(hhmm.substr(0, 2)) * 60 + std::stoi(hhmm.substr(3, 2));    int crossing = 0;    for (int i = 0; i < n; i++) {        std::string name, off;        std::cin >> name >> off;        // "-03:30"의 부호는 시와 분 모두에 붙는다        int t = start + (off[0] == '-' ? -1 : 1) * (std::stoi(off.substr(1, 2)) * 60 + std::stoi(off.substr(4, 2)));        std::cout << name << ' ' << local(t) << ' ' << local(t + d) << '\n';        if (dayShift(t) != dayShift(t + d)) crossing++;    }    std::cout << crossing << '\n';}
Cmain.c · 37 줄
#include <stdio.h> #define DAY 1440 /* C의 %는 0 쪽으로 버린 나눗셈의 나머지라 음수가 나올 수 있다 → 더해서 다시 나눈다 */int minute_of_day(int t) { return (t % DAY + DAY) % DAY; } /* 날짜 차이는 내림이어야 한다: t - (그날의 분)은 1440으로 딱 나누어떨어진다 */int day_shift(int t) { return (t - minute_of_day(t)) / DAY; } void print_local(int t) {    int m = minute_of_day(t), day = day_shift(t);    printf("%02d:%02d %s%d", m / 60, m % 60, day > 0 ? "+" : "", day);} int main(void) {    int sh, sm, d, n;    scanf("%d:%d %d", &sh, &sm, &d);    scanf("%d", &n);    int start = sh * 60 + sm, crossing = 0;    for (int i = 0; i < n; i++) {        char name[32], sign;        int oh, om;        scanf("%31s %c%d:%d", name, &sign, &oh, &om);        /* "-03:30"의 부호는 시와 분 모두에 붙는다 */        int off = (sign == '-' ? -1 : 1) * (oh * 60 + om);        int t = start + off;        printf("%s ", name);        print_local(t);        printf(" ");        print_local(t + d);        printf("\n");        if (day_shift(t) != day_shift(t + d)) crossing++;    }    printf("%d\n", crossing);    return 0;}
Craykonmain.cray · 51 줄
// "-03:30"의 부호는 시와 분 모두에 붙는다: -(3 * 60 + 30) = -210차이분(s){    분 = s.부분문자열(1, 2).수로변환() * 60 + s.부분문자열(4, 2).수로변환();    만약 (s.부분문자열(0, 1) == "-")    {        돌려주기 -분;    }    돌려주기 분;} // 크레이콘의 %도 JS처럼 음수를 남긴다(-50 % 1440 == -50) → 더해서 다시 나눈다그날의분(t){    돌려주기 (t % 1440 + 1440) % 1440;} // 날짜 차이는 내림: t - (그날의 분)은 1440으로 딱 나누어떨어진다날짜차이(t){    돌려주기 (t - 그날의분(t)) / 1440;} 시각(t){    m = 그날의분(t);    day = 날짜차이(t);    표 = "" + day;    만약 (day > 0)    {        표 = "+" + day;    }    돌려주기 ("" + 수학.몫(m, 60)).앞채우기(2, "0") + ":" + ("" + m % 60).앞채우기(2, "0") + " " + 표;} 첫줄 = 콘솔.줄읽기().나누기();start = 첫줄[0].부분문자열(0, 2).수로변환() * 60 + 첫줄[0].부분문자열(3, 2).수로변환();d = 첫줄[1].수로변환();n = 콘솔.줄읽기().수로변환();crossing = 0;반복 (i = 0; i < n; i++){    칸 = 콘솔.줄읽기().나누기();    t = start + 차이분(칸[1]);    콘솔.쓰기(칸[0] + " " + 시각(t) + " " + 시각(t + d));    만약 (날짜차이(t) != 날짜차이(t + d))    {        crossing += 1;    }}콘솔.쓰기(crossing);