跳转至

BISHI16 计算一年中的第几天

入门通过率 61.97%python3样例通过牛客 AC模拟基础数学

牛客原题  源码

讲解章节条件与循环模拟

一句话

给定年月日,求它是当年的第几天。

解题思路

这题考什么

两件事:闰年判定,以及「用前缀和代替循环累加」的习惯。 第 d 天 = 前面 m-1 个整月的天数之和 + d,再看 2 月是否要补 1 天。 前面那部分只跟月份有关,所以把 12 个月的天数前缀和预先打成常量表 PRE, 每组数据一次查表即可,不必每次都从 1 月循环加到 m-1 月。

另外这是一道「不给组数、读到文件末尾为止」的多组数据题—— 在牛客的早期入门题里非常常见,处理方式见 20-输入输出处理

数据规模与复杂度

每组 O(1),总复杂度与数据组数成正比。 组数没有明确上界,所以仍然按快读的方式写:一次性 sys.stdin.buffer.read().split() 拿到全部 token,输出攒起来一次 write。

坑在哪

  1. 题面没有给数据组数,只说「输入可能有多组测试数据」, 写 for i in range(n) 就无从下手,必须一直读到 EOF。 一次性 read().split() 最省事:行尾空白、空行、末尾换行全被 split 吃掉, 不必像 while True: line = input() 那样自己捕获 EOFError;
  2. 闰年的判定是「能被 4 整除且不能被 100 整除,或能被 400 整除」, 三个条件缺一不可。只写 y % 4 == 0 会把 1900 年当成闰年;
  3. 闰年多出来的那一天在 2 月 29 日,所以只有 m > 2 时才 +1。 写成 m >= 2 会让 2 月份的日期整体多算一天;
  4. 循环的终止条件写成 len(data) - 2 是为了保证每次都能完整取到三个 token, 输入末尾若多了零星空白也不会越界。

样例复核

1990 9 20:1990 不是闰年,PRE[9] = 243,243 + 20 = 263; 2000 5 1:2000 能被 400 整除是闰年,PRE[5] = 120,120 + 1 + 1 = 122。 与样例一致。

参考实现

solutions/BISHI16.py
import sys

# 每月天数的前缀和(平年):PRE[m] = 1..m-1 月的总天数
PRE = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]

data = sys.stdin.buffer.read().split()
out = []
# 每三个 token 是一组(年、月、日);上界取 len(data)-2 保证能取满三个
for i in range(0, len(data) - 2, 3):
    y, m, d = int(data[i]), int(data[i + 1]), int(data[i + 2])
    leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
    # 前 m-1 个月的天数 + 当月第 d 天,闰年且已过 2 月才补上 2 月 29 日
    out.append(PRE[m] + d + (1 if leap and m > 2 else 0))
sys.stdout.write("\n".join(map(str, out)) + "\n")
[:octicons-arrow-left-16: BISHI15](BISHI15.md) [BISHI17 :octicons-arrow-right-16:](BISHI17.md)