☰
洛谷P5707:C语言时间计算与向上取整的经典入门题解析
2026/10/5 12:01:10 网站建设 项目流程

洛谷P5707【深基2.例12】上学迟到,是我见过最能筛人的入门题之一。题面一句话就能看懂,完整C语言代码写下来不到20行,可它在洛谷入门题单里,WA率一直不低,很多新手反复提交都找不到原因。这道题真正考的并不是语法,而是把一个生活场景拆成数学表达式,再老老实实翻译成代码。下面就把“最晚几点起床”从读题到AC的完整链路拆给你看,适合刚刷完循环和分支、想用一道综合题检验自己的C语言初学者。

1. 先把题目“翻译”成人话:它到底在考什么

题目背景是这样的:主角从家出发去学校,家到学校的路程是 s 米,他走路的速度是 v 米/分钟,上课时间是早上8:00。这个人习惯提前10分钟到校。输入给出 s 和 v,要求输出他最晚几点起床。

这里有个点值得停下来想一下:题目说的是“提前10分钟到校”,不是“提前10分钟起床”。也就是说,10分钟是算在路上的总耗时里的,或者说到了学校之后要有10分钟缓冲。这一点理解错了,后面整个公式都会偏。

再看输入输出:

  • 输入:一行两个整数 s, v
  • 输出:一行 HH:MM

看到 HH:MM 就应该反应过来,题目对输出格式有严格要求:小时和分钟都必须两位,不足两位补前导0,比如 07:49,而不是 7:49。

1.1 题面与输入输出的真实含义

把题面里的关键信息对应到代码里的变量,是做题的第一步。我一般在草稿纸上列一张这样的对照表:

题面信息含义代码对应
s 米路程输入整数 s
v 米/分钟速度输入整数 v
上课时间 8:00时间基准点8 × 60 = 480 分钟
提前10分钟到校额外缓冲总耗时里再加10分钟
HH:MM输出格式printf("%02d:%02d", h, m)

把时间全部换算成分钟,是这类时间题最通用的做法。为什么不直接用 8 小时去减?因为后面涉及分钟和小时两个单位,分开算很容易出现进位、借位错误,比如“3点60分”这种根本不存在的时间格式。统一成分钟之后,所有计算都在整数范围内完成,最后再一次性拆成小时和分钟,思路会清晰很多。

1.2 为什么这道题是“入门但不简单”的典型

坦白说,这道题的语法难度几乎为零:只需要会 scanf、printf、if、整数除法。但它把好几个最容易在入门阶段被忽略的数学边界集中到了一起。

第一是除法方向。C语言的 / 对整数是向下取整,而题目要的是向上取整,这一点不搞清楚,样例可能侥幸过,换一组数据就WA。

第二是减法方向。从8:00往前推,如果耗时太长,减出来可能是负数,负数分钟怎么转成时间格式,是这道题真正考验逻辑的地方。

第三是格式化。很多新手代码逻辑全对,最终挂在 7:09 输出成 7:9 这种格式问题上。这些坑单拎出来每个都很小,但叠在一起,就是一道经典的“入门杀”题目。

2. 数学建模:从“最晚几点起床”到一条公式

2.1 先把时间统一成分钟

先定义两个基础量:

  • 上学路上耗时:travel = 路程 / 速度(分钟,向上取整)
  • 总耗时:total = travel + 10

然后起床时刻就是:

wake = 480 - total

这里 480 就是早上8:00的分钟表示:8 × 60 = 480。如果 wake 是正数,说明当天起床;如果 wake 是负数,说明从8点往前推不够推,得从前一天晚上开始算。

举个例子帮助理解。s = 1000,v = 100,travel = 1000 / 100 = 10,total = 10 + 10 = 20,wake = 480 - 20 = 460。460分钟 = 7小时40分钟,也就是 07:40。这个结果很直观。

但如果 s = 480,v = 1,travel = 480,total = 490。480 - 490 = -10,这是一个负数。这个负10分钟代表什么?代表从8:00倒退490分钟,已经退到前一天晚上去了。所以必须加上24小时,也就是1440分钟,变成 1430,再转成 23:50。这就是“跨天”处理。

2.2 向上取整:最容易漏掉的一环

题目里有一个隐含条件容易被经验不足的人忽略:速度和时间都是按分钟计算的,但路程除以速度并不一定能整除。比如 10 米的路,速度是 3 米/分钟,需要 10 ÷ 3 ≈ 3.33 分钟。可代码里根本没有小数分钟的概念,最小单位就是1分钟。你只走完了3分钟,还剩 0.33 分钟的路没走完,这0.33分钟也得算1分钟,所以耗时是4分钟。

这就是向上取整。C语言里 10 / 3 的结果是 3,如果直接拿这个 3 去用,就意味着少算了1分钟。在实际生活中只差1分钟可能没什么,但在题目里,少算1分钟就可能把起床时间算晚,导致迟到,所以这是致命错误。

向上取整的原理,可以理解成你约朋友8点见面,路上要走3分零1秒,你不可能3分钟就到,答案是4分钟。只要还有1秒没走完,就得按一整分钟算,这就是题目里时间的颗粒度。

那怎么在整数范围内做向上取整?两个办法:

  1. 判断能否整除:if (s % v == 0) travel = s / v; else travel = s / v + 1;
  2. 公式法:travel = (s + v - 1) / v;

第二种方法很多人不理解。它的原理是:整数除法向下取整,如果想向上取整,就在被除数上人为加一个“足够大但不至于影响整除部分”的数,也就是 v - 1。只要原式有余数,加上 v - 1 之后商就比原来多1;如果刚好整除,加上 v - 1 也不会让商发生变化。这个技巧在很多竞赛题里都很常用,建议直接记住。

2.3 跨天处理:起床时间可能在前一天晚上

wake = 480 - total,total 如果超过480,wake 就小于0。这时最晚起床时间其实落在前一天,需要加上 1440 把它拉回 0 到 23:59 这个合法区间。

新手常见的错误是直接输出负数,比如算出 -10,然后打印 -10 / 60 和 -10 % 60,得到类似 -0:-10 的奇怪结果。正确做法一定是在输出前判断:如果 wake < 0,就 wake += 1440。

严谨一点想:如果 total 特别大,wake 可能小于 -1440,原则上应该用 while 循环反复加 1440,直到结果非负。洛谷这道题的数据范围一般不会出现跨越两天以上的情况,用 if 加一次就够了。不过我在代码里更推荐写 while,逻辑更严谨,也不会多花任何成本。

3. 关键一步:向上取整、时间换算与跨天处理的C语言实现

3.1 向上取整的两种写法对比

先比较两种写法在实际使用中的区别。

写法一:if / else

int travel; if (s % v == 0) { travel = s / v; } else { travel = s / v + 1; }

写法二:公式

int travel = (s + v - 1) / v;

从可读性来说,写法一更直白,新手能一眼看懂“不能整除就多算1分钟”。从简洁性和防错角度来说,写法二更好,少了两行分支,也少了一种写错分支条件的可能。

我个人推荐写法二,但前提是你真的理解它为什么成立,而不是死记公式。如果不理解,建议先写写法一,确保逻辑正确,再尝试用公式替换。

3.2 从公式到代码:逐步翻译

把前面推导的公式翻译成C语言,核心流程只有四步:

  1. 读入 s 和 v;
  2. 算路程耗时 travel,注意向上取整;
  3. 算 wake = 480 - travel - 10,负数加1440;
  4. 用 %02d 格式化输出小时和分钟。

第3步里我用的是 480 - travel - 10,逻辑上等价于 480 - (travel + 10)。推荐写成 480 - travel - 10 这种展开形式,因为一眼就能看出“上课时间480减去路程耗时,再减去10分钟缓冲”。当然写成 480 - (travel + 10) 也行,看个人习惯。

这里还有几个容易被忽略的小地方。输入用scanf("%d %d", &s, &v)就够,竞赛环境下可以不检查返回值,但如果你想写成更健壮的样子,可以判断一下返回值是不是2,不是就提前结束。变量命名也值得注意,travel、total、wake 这种有明确含义的名字,会让排查问题轻松很多,别用 t、a 这种只有你自己看得懂的缩写。

3.3 格式化输出为什么用 %02d

到了输出这一步,仍然有坑。要求是 HH:MM,小时和分钟都占两位。7点49分要输出 07:49,不是 7:49。C语言里%d不会自动补前导0,写%02d才会:如果数值不足两位,左边补0;如果超过两位,照常输出。

需要注意顺序:先处理跨天,再输出。有些新手先把 wake 转成小时和分钟,再在负数状态下直接打印,结果既没补0也没跨天,整个输出完全错乱。正确流程是:判断并修正 wake,然后 h = wake / 60,m = wake % 60,最后 printf("%02d:%02d", h, m)。

4. 边界条件与隐藏陷阱:整除、补零、跨天、除零

4.1 几个必测的输入用例

把代码写完以后,强烈建议先用下面这几个用例自测,全过了再提交:

输入期望输出说明了什么
1 107:49最短路径,验证基础计算
10 307:46验证向上取整:10/3要按4分钟算
9 307:47整除场景:3分钟整,不用+1
100 106:10路程较长但仍在当天
480 123:50验证跨天:总耗时超过480分钟

第2个用例最关键。如果代码用了普通整数除法 10/3,会算出13分钟总耗时,输出 07:47;但正确答案是14分钟总耗时,输出 07:46。这一个用例就能筛掉所有忘记向上取整的写法。

第5个用例用来验证跨天。s=480,v=1 时,走路要480分钟,加10分钟缓冲一共490分钟,8:00往前490分钟就是前一天23:50。如果你的代码看到 wake = -10 就不知所措,或者直接输出负数,这个用例会把你打回原形。

4.2 新手最常见的四类WA原因

我在一些新手交流区观察下来,这道题的WA基本集中在四类原因:

  1. 没用向上取整。直接用 s / v,被带小数的情况坑了。
  2. 忘了扣10分钟。“提前10分钟到校”这句话,很多人把它当成废话,计算结果比正确答案晚10分钟。
  3. 跨天只加一次或者干脆不加。要么输出负数,要么格式错乱。
  4. 输出格式错误。没写 %02d,输出 7:49 而不是 07:49。

前两类是逻辑错误,后两类是细节错误。但洛谷的评测不区分错误类型,只要输出跟标准答案不一致,就是WA。这也是为什么这道题看起来简单,却总有新手反复提交失败的原因——四个坑踩任何一个都过不了。

4.3 关于除零:v=0 时的处理思路

严格来说,这道题里速度 v 是正整数,题目数据不会出现0。但“除数不能为0”是C语言里最基本的常识,万一你拿着这道题的代码去改造成其他场景,或者自己扩展测试,碰到 v=0 时s / v会直接触发运行时错误,程序崩溃。

如果想把代码写得防御性强一点,可以在读入后加一个特判:

if (v == 0) { // 速度为0,题目不会允许,这里可以按无法到达处理 printf("00:00\n"); return 0; }

不过竞赛环境下不需要这么写,因为题目保证 v > 0。我提这个,主要是想说清楚:整数除法的前提是分母不为0,实际开发中这个检查很便宜,值得养成习惯。

4.4 数据范围与 long long 的取舍

洛谷这道题的常规数据规模下,int 类型足够。32位 int 最大约21亿,而 s 和 v 通常是几万级别,travel 和 wake 的中间计算也远不会溢出。

但如果你刷到类似题目,发现数据范围可能达到 10^9 甚至更大,就要立刻想到用 long long。改起来也简单,把变量类型换成 long long,scanf 和 printf 的格式符换成 %lld,处理思路完全不变:

#include <stdio.h> int main(void) { long long s, v; scanf("%lld %lld", &s, &v); long long travel = (s + v - 1) / v; long long wake = 480 - travel - 10; while (wake < 0) { wake += 24 * 60; } printf("%02lld:%02lld\n", wake / 60, wake % 60); return 0; }

养成“先瞄一眼数据范围再定类型”的习惯,能帮你避掉一整类溢出WA。

5. 完整代码与提交心得:如何做到提交一次过

5.1 可以直接提交的完整C语言代码

综合前面的分析,一份可以直接提交洛谷P5707的C语言代码如下:

#include <stdio.h> int main(void) { int s, v; scanf("%d %d", &s, &v); // 路程耗时,向上取整 int travel = (s + v - 1) / v; // 总耗时 = 走路时间 + 提前到校的10分钟 int total = travel + 10; // 上课时间按分钟计是 8 * 60 = 480 int wake = 480 - total; // 如果为负数,说明要从前一天晚上开始算,拉回24小时内 while (wake < 0) { wake += 24 * 60; } printf("%02d:%02d\n", wake / 60, wake % 60); return 0; }

这份代码的核心就一行公式:(s + v - 1) / v,配合一个 while 做跨天修正,最后用%02d保证输出格式。整个程序没有任何高深语法,却把整数运算、分支循环、格式控制全部覆盖到了。

5.2 我在做题时坚持的三个习惯

最后分享三个我自己的小习惯,不仅是这道题,刷所有洛谷入门题都适用。

第一个习惯:先手算样例,再写代码。拿到题目先在草稿纸上把公式列出来,用题目给的样例手算一遍,确认结果与题目输出一致,再动手。这样能避免“代码写完了才发现思路从一开始就错了”的浪费。

第二个习惯:提交前自定义三个边界用例。一个最小输入、一个整除边界、一个跨天用例。这道题就是 (1,1)、(9,3)、(480,1),三组数据一测,四类WA原因当场就能发现。

第三个习惯:WA 之后先看数据,别看题解。洛谷的评测会给出错误用例,把错误用例带进本地程序跑一遍,用 printf 把 travel、total、wake 中间值都打出来,一般一眼就能定位问题。实在看不出来再上调试器。看题解是最后的手段,因为自己排查一遍,比看十次题解都记得牢。

另外,如果你偶尔遇到“提交失败无法解析路由对象”之类的提示,先别慌。这是网站服务端的偶发问题,和你的代码无关,稍后重新提交就好,不要在本地反复改代码浪费时间。

5.3 一个值得做的扩展练习

最后再啰嗦一句:这道题的价值不在代码量,而在它强迫你想清楚“整数除法的方向”“跨天要怎么折算”“输出格式如何保证”这三件事。想明白这三个点,后面再遇到日期时间类的题,比如更复杂的时钟问题、日历问题,思路都会顺很多。

你可以自己把题目改一版练手:如果上学时间是 8:10,或者要求提前15分钟到校,公式该怎么调整?如果输入给的是起床时间,让你判断会不会迟到,反过来怎么比较?想通这些扩展,P5707 才算真正刷透了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询