{ }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()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 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);