{ }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 のように書く。
最後の行に、開始の日付の差と終了の日付の差が違う都市の数、つまり会議中にその都市で午前 0 時を過ぎる都市の数を出力せよ。

入力
00:00 ≤ 開始時刻 ≤ 23:59、1 ≤ D ≤ 1,440、1 ≤ n ≤ 1,000、都市の名前は英字 1〜20 文字、-12:00 ≤ 時差 ≤ +14:00 で、時差の分は 00・15・30・45 のどれか
出力
都市ごとに一行(名前、開始時刻、日付の差、終了時刻、日付の差)、最後の行に午前 0 時を過ぎる都市の数
例 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()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 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 です(Craykon の % も JS と同じです)。
そこで (t % 1440 + 1440) % 1440 のように 1440 を足してもう一度割るか、Java なら Math.floorMod(t, 1440) を使います。
日付の差も同じ落とし穴です。
C・C++・Java・C# の -50 / 1440 は 0 なので、前日が消えます(JS・TS・Craykon の / は -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')、Craykon 前埋め(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);