01 / 本课学习路线
本课学习路线
阅读与推演约 115 分钟,练习约 55 分钟,进阶练习另需约 15 分钟
02 / 学习目标与先修自测
学完本课你能做什么,以及开始前需要会什么
练习把题目数据读进程序、按规则算出答案,再按要求输出和提交。
| 学完后能做的事 | 正文位置 | 检查方式 |
|---|---|---|
| 按题目的输入格式选对读法(整行读 / 一次读完按空白切 / 混合 / 读到结尾) | 第 04 节 | 自查第 2 条、练习 1 |
| 把公里与米、小时与分钟统一后再比较,并用整数交叉相乘避免浮点误差 | 第 05 节 | 自查第 4 条、练习 2 |
| 不查看参考代码写出完整程序并通过(AC)P2480、P2484 | 第 06 节 | 必做任务 1、2 |
| 输出与样例逐字符一致:无提示语、无调试输出、大小写与换行正确 | 第 07 节 | 自查第 5 条 |
| 完成一次真实的「本地比对 → 站内提交 → 看结果 → 改错重交」,看到提交结果(编译错误、答案错误、运行错误、超时、格式错误)能说出先查哪里 | 第 03、07、08 节 | 自查第 3 条、第 07 节的纠错演示 |
先修自测:下面 5 题请先自己写答案,再展开对照。答不出的按括号里的位置补看再回来——本课假定你会变量、条件、循环、函数和列表,不假定你用过判题系统。
| 题号 | 题目 | 补看位置 |
|---|---|---|
| 自测 1 | "3 5 7".split() 的结果是什么?它的元素是什么类型? | 字符串与 列表 |
| 自测 2 | 把上一题的结果变成整数列表 [3, 5, 7],写出一种写法。 | 列表常用操作(map 的用法在本节答案里直接给出) |
| 自测 3 | print(3, 7) 和 print("3 7") 输出的文本一样吗?print 默认在末尾加什么? | 输入与输出,本节第 2 步的按钮直达 |
| 自测 4 | "hello world\n".rstrip("\n") 得到什么?为什么不用 strip()? | 字符串 |
| 自测 5 | 写一个函数 add(a, b) 返回两数之和,并调用它打印 add(1, 2)。 | 函数 |
补看位置都是本站「看动画学 Python」的独立课页(总入口 看动画学 Python);iter / next 按需取记号的写法在第 04 节模板 3 的表里单独解释。
展开先修自测答案
自测 1:得到列表 ["3", "5", "7"],元素是字符串,不是整数。不带参数的 split() 按任意空白(空格、制表符、换行)切分,并自动跳过连续空白。
自测 2:[int(x) for x in "3 5 7".split()] 或 list(map(int, "3 5 7".split())),都得到 [3, 5, 7]。
自测 3:一样,都输出 3 7。print 用一个空格连接多个参数,末尾加一个换行 \n。
自测 4:得到 "hello world"。rstrip("\n") 只去掉结尾的换行;strip() 还会去掉两端的空格,而有的题目行首行尾的空格是内容的一部分(本课的 P2484 就要整行原样统计)。
自测 5:def add(a, b): return a + b,然后 print(add(1, 2)) 输出 3。
03 / 概念与术语
标准输入、标准输出、判题机与六种提交结果
在函数模式(LeetCode 那种「补全一个函数」)里,读入和输出由平台代劳;在标准输入输出模式里,这两件事都由你的程序完成,判题机只看你写到标准输出的文本。
| 术语 | 含义 | 在 Python 里对应什么 |
|---|---|---|
| 标准输入(stdin) | 程序启动后可以读取的一条文本流,判题机把测试数据从这里送进来 | sys.stdin、input() |
| 标准输出(stdout) | 程序写出的文本流,判题机只比对这里的内容 | print、sys.stdout.write |
| 记号(token) | 按空白切开后的一小段文本,如 3 5\n7 切出三个记号 3、5、7 | text.split() 的每个元素 |
| 行 | 以换行符结束的一段文本;一行是否是一个语义单位由题目决定 | sys.stdin.readline()、splitlines() |
| 判题机 | 编译或启动你的程序、喂入测试数据、收集输出、与标准答案比对的系统 | — |
| 测试数据 | 一道题通常有多组输入和对应的标准答案,全部通过才是通过(AC) | — |
| 时限 / 内存限制 | 程序每组数据允许的运行时间和内存上限,超出即超时或内存超限 | — |
你点「提交」之后,判题机做的事只有四步:① 编译或启动程序,放进限定了时间和内存的沙箱(隔离的运行环境);② 把题目的测试输入送进标准输入,在资源限制内运行;③ 收集退出码和标准输出;④ 和标准答案比对。比较的宽严由该题的判题器决定:有的按整份输出严格比较,有的按行比较并忽略行末空白。题目没有要求的提示语不要输出。
| 结果 | 含义 | 发生在哪一步 | 先查什么 |
|---|---|---|---|
| 编译错误(CE) | 程序在编译或解释阶段就未通过 | ① 编译/启动 | Python 多为语法或缩进错误;本地先跑一遍样例 |
| 运行错误(RE) | 程序中途异常退出(未捕获异常、下标越界、读入越过末尾) | ② 运行 | 读入次数是否比输入行数多;下标是否越界 |
| 超时(TLE) | 跑满时限还没结束 | ② 运行 | 复杂度是否与数据范围匹配;是否在大循环里逐次调用 input() |
| 答案错误(WA) | 输出和标准答案不同 | ④ 比对 | 单位换算、固定文本大小写、是否混入提示语 |
| 格式错误(PE) | 数字全对,空格或换行不对 | ④ 比对 | 对照样例逐字符检查行尾与末尾换行 |
| 通过(AC) | 全部测试数据都通过 | ④ 比对 | 记下这题的读法和你第一次出错的原因 |
04 / 输入与复杂度
按输入格式选择读法
先看清输入格式,再选读法:整行有意义就整行读,只关心数字就一次读完再拆。四段模板互斥,按题选一种。
四种读法模板(互斥,按题选用)
Python# 四选一:以下模板互斥,按题目输入格式选择一种,不要连着运行
import sys
# 1) 逐行读:这一行本身有意义(句子、含空格的文本、一条 URL)
line = sys.stdin.readline().rstrip("\n")
# 2) 一次读完,按空白拆成一个个记号(token):只关心数字
data = sys.stdin.buffer.read().split()
# 3) 混合格式:第一行 n,后面 n 组 —— 用迭代器按需取
data = sys.stdin.buffer.read().split()
it = iter(data)
n = int(next(it))
for _ in range(n):
a, b = int(next(it)), int(next(it))
print(a + b)
# 4) 读到文件尾为止(题目没给组数时)
for line in sys.stdin:
a, b = map(int, line.split())
print(a + b)第 3 种写法的好处:一次读取全部内容再按空白字符分隔,不受原始换行位置影响——无论 n 组数据分别位于 n 行,还是集中在同一行,读取代码都相同,只有输出需要调整。第 4 种用于「读到文件尾为止」的题目。
| 读法 | 对输入 3 5⏎7⏎ 只做读取得到 | 适用的题目输入 |
|---|---|---|
1) 整行读 readline().rstrip("\n") | 字符串 "3 5"(第二次再调用得到 "7") | 这一行本身有意义:句子、含空格的文本、URL(P2484、P2485) |
2) 一次读完按空白切 buffer.read().split() | 列表 [b"3", b"5", b"7"],三个字节串,int() 可直接转换 | 只关心数字,行的划分无意义(P2480) |
| 3) 混合:先读组数再按需取 | n = 3,之后 next(it) 依次得到 b"5"、b"7"。注意:这段输入不满足模板 3「n 组、每组两个数」的约定(需要 6 个记号、只有 2 个),把模板 3 整段运行会先输出 12,再在取第 3 个数时抛出 StopIteration | 第一行 n,后面 n 组数据(练习 5 的多组 A+B) |
4) 读到文件尾 for line in sys.stdin | 第一轮 line = "3 5\n",第二轮 "7\n",读完即停。注意:模板 4 要求每行两个数,第二行只有一个数,整段运行会先输出 8,再在第二轮解包时抛出 ValueError | 题目没给组数,读到没有为止 |
⏎ 表示换行符。判断标准只有一条:题目输入里的「行」是不是一个语义单位——是就整行读(读法 1 或 4),不是就一次读完按记号取(读法 2 或 3)。模板 3、4 各自要求的输入形状见下表。
| 模板 | 合法输入示例 | 输出 | 循环怎么结束 | 输入不合规时 |
|---|---|---|---|---|
| 3) 先读组数再按需取 | 2⏎1 2⏎3 4⏎(第一个记号是组数 2,随后每组两个数) | 3⏎7⏎ | 按组数 n 循环 n 次后结束;iter / next 每次取下一个记号 | 记号不够时 next(it) 抛出 StopIteration(上面那段两行输入就是这种情况) |
| 4) 读到文件尾 | 1 2⏎3 4⏎(每行两个数,行数不限) | 3⏎7⏎ | 标准输入读完(文件尾)自动结束 | 某行不是两个数时 a, b = ... 解包抛出 ValueError |
两个模板对「1 2 / 3 4」这组数据都输出 3 和 7,区别只在组数是否由第一行给出。本地试跑时按这两种输入形状各准备一个文件。
| 数据范围 | 通常优先考虑 |
|---|---|
| n ≤ 20 | 枚举或回溯可能可行 |
| n ≤ 2,000 | O(n²) 可能可行 |
| n ≤ 200,000 | 优先 O(n) 或 O(n log n) |
| n ≤ 1,000,000 | 通常需要接近 O(n) |
按 Python 每秒约 10⁷ 次基本操作、时限 1 秒粗估——数量级筛选,只用来排除明显超时的方案。本课三道题都是一趟扫描或常数次运算,按量级估算远小于时限;这个判断动作是为后面的课准备的。
输出规则:判题机只比对标准输出
「请输入两个数:」这种提示语会原样进入比对,结果一定是答案错误(WA)。输出写成和样例一模一样、最后一个换行结束;多组输出、分隔符和小数位数逐项核对。提交前把调试用的打印语句(print)全部删掉。
补充学习(选学)判题机在做什么约 5 分钟判题的四个步骤,以及每种判题结果对应哪一步
你点「提交」之后,判题机做的事只有四步:编译或启动程序,放进限定了时间和内存的沙箱(隔离的运行环境);把题目的测试输入送进标准输入,在资源限制内运行;收集退出码和标准输出;和标准答案比对。
每一种判题结果都对应这四步里的一个位置:程序在编译或解释阶段就未通过(语法、缩进)是编译错误(CE);程序中途异常退出(未捕获异常、下标越界)是运行错误(RE);跑满时限还没结束是超时(TLE);输出和答案不同是答案错误(WA);数字全对、空格或换行不对是格式错误(PE)。一道题通常有多组测试数据,全部通过才是通过(AC)。
补充学习(选学)四种读法,各在什么时候用约 6 分钟为什么一次读完再切分通常最快,什么时候必须逐行读
三种读法的差别在于每个数值之外还要多做多少次工作,而这部分工作按「行数」和「函数调用次数」算,不按数值个数算。逐行读取函数(input)每调用一次读一行:这一行要单独解码成文本、去掉换行,再切分交给 int(),有多少行就调用多少次。逐行迭代标准输入(for line in sys.stdin)同样按行处理,只是省掉了逐次调用 input() 的开销。一次性读取再拆分(sys.stdin.buffer.read().split())只做一次读取和一次切分,切出来的元素是字节串(bytes),int() 可以直接接收字节,不需要先把整份输入解码成文本。所以差别取决于行的分布:10⁶ 个整数排成 10⁶ 行时,前两种读法要按行处理 10⁶ 次;同样 10⁶ 个整数排成一行时,行数只有 1,三者的差别就小得多。整数转换本身是 10⁶ 次,三种读法都躲不掉,省下的只是按行处理与逐次调用那部分。具体快多少取决于机器、解释器版本与输入形态,需要具体数字时请在自己的环境里计时测量。
但「快」不是唯一标准:当这一行本身有意义(一条 URL、一句含空格的文本,比如本课的 P2485),必须逐行读,split 会把它拆碎。判断标准只有一条——题目输入里的「行」是不是一个语义单位。
补充学习(选学)把数据范围换算成秒约 5 分钟n → 运算次数 → 秒的换算表,附两个算例
| 数据范围 | 操作次数 | 估计耗时 | 结论 |
|---|---|---|---|
| n ≤ 2,000 | n² = 4×10⁶ | 0.4 秒 | 双重循环可能接近时限,需结合语言和常数验证 |
| n ≤ 200,000 | n² = 4×10¹⁰ | 4000 秒 | 双重循环直接超时 |
| n ≤ 200,000 | n log n ≈ 3.5×10⁶ | 0.35 秒 | 排序一次通常可行,仍需结合后续操作评估 |
| n ≤ 1,000,000 | n = 10⁶ | 0.1 秒 | 一趟扫描 |
只用一个数换算:Python 保守按每秒 10⁷ 次基本操作粗估。把 n 代进复杂度、除以 10⁷、和时限比。
05 / 完整手算例
P2480 从读入到输出:题目样例 50 5 500 30 90
题目:医院 A 距离 X 公里,只能坐计程车(速度 M 米/分钟,先等 L 分钟);医院 B 距离 Y 公里,只能步行(速度 N 米/分钟)。输入一行五个整数 X Y M L N,输出更快的方式:Taxi / Walk / Same。下面用题目页的样例走完每一步。
| 步骤 | 做什么 | 样例上的结果 |
|---|---|---|
| 1 读入 | 一行五个整数,按空白切分后转成 int | X=50, Y=5, M=500, L=30, N=90 |
| 2 单位统一 | 距离是公里,速度是米/分钟 → 公里 × 1000 变成米 | A:50 000 米;B:5 000 米 |
| 3 计程车耗时 | 路程 ÷ 速度 + 等待 | 50 000 ÷ 500 + 30 = 100 + 30 = 130 分钟 |
| 4 步行耗时 | 路程 ÷ 速度 | 5 000 ÷ 90 = 55.5556 分钟 |
| 5 比较 | 130 > 55.5556 → 步行更快 | 输出 Walk(与题目页样例输出一致) |
输出文本必须与题目要求逐字符一致:Walk 首字母大写、没有句号、没有提示语,末尾一个换行。
第 3、4 步的除法会产生小数(55.5556…),而第 5 步只需要比大小。把两个时间同乘 M×N,除法就消失了:计程车 = 1000·X·N + L·M·N,步行 = 1000·Y·M。样例上:计程车 = 1000×50×90 + 30×500×90 = 4 500 000 + 1 350 000 = 5 850 000;步行 = 1000×5×500 = 2 500 000;前者更大,仍是 Walk。全程只用整数乘加,判断「相等」时没有浮点误差的隐患——题目页的参考题解也是这样写的。
| 量 | 计程车(X=1, M=500, L=1) | 步行(Y=3, N=1000) |
|---|---|---|
| 按分钟算 | 1000 ÷ 500 + 1 = 3 | 3000 ÷ 1000 = 3 |
| 同乘 M×N = 500 000 | 1000×1×1000 + 1×500×1000 = 1 500 000 | 1000×3×500 = 1 500 000 |
| 结论 | 两边相等 → 输出 Same |
「相等」分支必须单独写:只写 if taxi < walk … else … 会把 Same 的情况错判成 Walk。
06 / 从步骤到程序
把五个步骤写成代码:变量对照与三道题的完整参考程序
先对照下表看每个步骤落到哪几行代码,再自己写完整程序提交;三份完整参考程序在展开区里,写完并提交过一次再展开对照。
| 步骤 | 代码 | 说明 |
|---|---|---|
| 读入五个整数 | X, Y, M, L, N = map(int, sys.stdin.readline().split()) | 读法 2 的单行版:一行按空白切成 5 个记号并转 int;个数不是 5 会抛出解包错误(ValueError) |
| 单位统一 + 耗时 | taxi = 1000 * X * N + L * M * N;walk = 1000 * Y * M | 两边同乘 M×N 后的整数式,见第 05 节 |
| 三分支比较 | if taxi < walk … elif taxi > walk … else … | 小于、大于、等于各一个分支,缺一不可 |
| 输出 | print("Taxi") 等 | 固定文本逐字符与题目一致 |
展开完整参考程序 1:P2480 最快到达医院的方法(先自己写完并提交一次,再展开对照)
完整程序:P2480(标准输入 → 标准输出)
Pythonimport sys
X, Y, M, L, N = map(int, sys.stdin.readline().split())
# 计程车:X 公里 = 1000*X 米,耗时 1000*X/M 分钟,再加等待 L 分钟
# 步行: Y 公里 = 1000*Y 米,耗时 1000*Y/N 分钟
# 只比大小,不需要真的做除法:两边同乘 M*N,全程整数运算,没有浮点误差
taxi = 1000 * X * N + L * M * N
walk = 1000 * Y * M
if taxi < walk:
print("Taxi")
elif taxi > walk:
print("Walk")
else:
print("Same")自测用例:题目页样例 50 5 500 30 90 → Walk;第 05 节的 1 3 500 1 1000 → Same;练习 2 的 2 1 1000 1 200 → Taxi。提交后以判题结果为准。
展开完整参考程序 2:P2484 计算某个字符出现次数
完整程序:P2484(标准输入 → 标准输出)
Pythonimport sys
s = sys.stdin.readline().rstrip("\n") # 第一行:待统计的字符串(可能含空格,整行读)
target = sys.stdin.readline().rstrip("\n") # 第二行:目标字符(只去掉换行,不去掉别的字符)
if target.isalpha():
# 目标是字母:大写和小写都算(与题目页参考题解一致;以题目页说明为准)
count = s.count(target.lower()) + s.count(target.upper())
else:
# 目标不是字母(数字、符号):只数完全相同的字符
count = s.count(target)
print(count)输入两行:第一行是字符串(可能含空格,用整行读,只去掉行尾换行),第二行是目标字符。目标是字母时大小写都计入(与题目页参考题解一致——题目页说明的那一句请逐字读,以它为准);不是字母时只数完全相同的字符。自拟输入 Hello World / l 得 3,abc123 / 1 得 1,AbcA / a 得 2。
为什么第二行不用 strip():目标字符如果是空格,strip() 会把它去掉变成空串,而 s.count("") 会返回 len(s)+1 这个毫无意义的数。只去换行(rstrip("\n"))才保得住这种输入。
展开完整参考程序 3:P2485 拼接 URL(进阶练习,做完再看)
完整程序:P2485(标准输入 → 标准输出)
Pythonimport sys
line = sys.stdin.readline().rstrip("\n")
prefix, suffix = line.split(",")
# 两段都按 "/" 切开,丢掉空片段(空片段来自开头、结尾或连续的 "/"),
# 再用一个 "/" 连接并以 "/" 开头——四种斜杠组合都归成同一种写法
parts = [p for p in prefix.split("/") if p] + [p for p in suffix.split("/") if p]
print("/" + "/".join(parts))题目页示例 /abc/,/bcd → /abc/bcd。把两段都按 / 切开、丢掉空片段、再用 / 连接并以 / 开头:都无 / 都有 / 只前有 / 只后有四种组合,以及某一段为空或只有 / 的情况,都归成同一种写法。自测用例:练习 3 表里的四种组合。题目页参考题解用「补首斜杠 + 去尾斜杠」的写法,两种写法输出相同。
07 / 输出规则、本地自查与首次提交
同一组答案的四种输出写法、提交前的逐字符比对,以及第一次提交怎么操作
打印函数(print)默认在末尾补一个换行,多个参数之间补一个空格。同一组答案,「每个答案一行」和「所有答案排在一行、空格分隔」只是换一种用法;用哪一种由题目的输出格式决定,不能凭习惯选。
四种输出写法(互斥,按题目要求选用)
Python# 以下四种写法互相独立,按题目要求的输出格式选一种
import sys
answers = [3, 7]
print(*answers) # 一行、空格分隔 -> "3 7"
print("\n".join(map(str, answers))) # 每个答案一行
sys.stdout.write("\n".join(map(str, answers)) + "\n") # 十万行以上:拼好一次写出
print(f"{2 / 3:.2f}") # 题目要求两位小数 -> "0.67"答案很多时(十万行以上),先把结果攒进列表、用换行拼接(join)成一个字符串再一次写出,比在循环里逐条 print 更省:十万行答案逐条 print 就是十万次写出调用,先拼接再一次写出只有一次。小数一律按题目要求的位数格式化,直接 print 一个浮点数位数不可控。
| 写法 | 写到标准输出的确切字符 | 适用的题目输出格式 |
|---|---|---|
print(*answers) | 3 7⏎ | 一行、空格分隔 |
print("\n".join(map(str, answers))) | 3⏎7⏎ | 每个答案一行 |
sys.stdout.write("\n".join(map(str, answers)) + "\n") | 3⏎7⏎(与上一行完全相同) | 答案极多时的同一格式 |
print(f"{2 / 3:.2f}") | 0.67⏎ | 题目要求两位小数 |
⏎ 表示换行符。sys.stdout.write 不会自动补换行,所以拼接后要自己在末尾补一个;题目要求每行一个答案时不要多补出一个空行。末尾换行和行末空格是否会被判错,取决于该题判题器的设置——稳妥做法是照样例输出原样对齐格式,本地再用严格比较自查一遍。
每次提交都要排队判题,本地先把样例跑通可以减少格式错误和无效提交。流程固定三步:把题目的样例输入和样例输出原样存成两个文件;用重定向把输入送进程序、把程序输出写进第三个文件;再逐字符比对程序输出与样例输出。
保存样例 → 重定向运行 → 逐字符比对
bash# 第一步:把题目样例原样存成两个文件
# in.txt 样例输入(不要自己加空行、不要改空格)
# expected.txt 样例输出(连最后一个换行也原样保留)
# 第二步:用重定向把 in.txt 送进标准输入,把程序输出写进 out.txt
python3 a.py < in.txt > out.txt
# 第三步:逐字符比对两个输出文件;没有任何输出就说明完全一致
diff out.txt expected.txt
# 只想先看一眼输出、不落文件时
python3 a.py < in.txt比较命令(diff)没有任何输出,就说明两个文件完全相同——包括行末空格和文件末尾的换行;有差异时它会指出第几行不同,据此回到输出段修改。这一步是本地的严格逐字节自查,用来发现格式差异;某个差异是否会被判错,仍以题目说明与该题判题器的设置为准。Windows 下的等价步骤见下方代码块。
Windows 下的同一套检查:PowerShell 与命令提示符
PowerShell# 第一步:与上面相同,把样例原样存成 in.txt 与 expected.txt
# 第二步:重定向运行。PowerShell 不支持 < 重定向,这一步借命令提示符执行
cmd /c "python a.py < in.txt > out.txt"
# 第三步(严格):按整份文件内容比较,空格、换行和文件末尾的换行都算进去
if ([IO.File]::ReadAllText("out.txt") -ceq [IO.File]::ReadAllText("expected.txt")) { "一致" } else { "不一致" }
# 第三步的另一种做法:Compare-Object 比较的是两组「行」,
# 行内容一致时它不报差异,因此看不出文件末尾换行的差别
Compare-Object (Get-Content out.txt) (Get-Content expected.txt)
# 命令提示符(cmd)里还可以用 fc 做逐字节比较,/w 则忽略空白
fc /b out.txt expected.txt三步与上面一一对应,只是命令不同。要注意两种比较的强度不一样:按整份文件内容比较(-ceq)和 fc /b 会把行末空格、文件末尾换行都算进去;Compare-Object 比较的是两组行,行内容相同就不报差异。想复现「末尾少一个换行」这类问题,用前两种。
逐字符比对能提前发现的,正是第一次提交最常见的几类问题:输出里混进了提示语或调试用的打印语句、行末多了一个空格、末尾少了或多了一个换行、多组数据只处理了第一组、下标基准与题目要求不一致。数据范围导致的超时不在其中——那要结合复杂度估算判断,不能靠样例发现。
第一次提交的完整操作(以 P2480 为例,第一次做题按这个顺序照做):
| 步骤 | 做什么 | 说明 |
|---|---|---|
| 1 建目录 | 在电脑上新建一个文件夹(例如桌面上的 oj),本课的文件都放在这里 | 三个文件必须在同一个文件夹里,后面的命令才找得到它们 |
| 2 存程序 | 把你写的完整程序(从 import sys 到最后一个 print)保存为该文件夹里的 a.py | 保存的是整份程序,不是某个函数;用任何文本编辑器都可以 |
| 3 存样例 | 把题目页的样例输入原样存成 in.txt,样例输出原样存成 expected.txt | 不要自己加空行或提示语;最后一个换行也保留 |
| 4 打开终端并进入该目录 | macOS:打开「终端」,输入 cd 加一个空格,把文件夹拖进终端窗口,回车。Windows:在文件夹的地址栏输入 cmd 回车 | 命令都要在这个目录里执行,否则会报「找不到文件」 |
| 5 本地比对 | 执行 python3 a.py < in.txt > out.txt,再执行 diff out.txt expected.txt(Windows 见上面的代码块) | diff 没有任何输出就是一致 |
| 6 站内提交 | 打开题目页(任务卡上的按钮),在右侧代码区确认语言是 Python,把整份 a.py 的内容粘贴进去,点「提交」 | 粘贴整份程序(含读入与输出);不是只贴函数,也不是贴输入数据 |
| 7 看结果 | 提交后页面显示本次结果(通过 / 答案错误 / 运行错误 / 超时 / 格式错误)和提交记录 | 记下第一次出错的结果类型,按第 03 节的表决定先查什么 |
纠错演示:故意把 Walk 写成 walk,看比对怎样发现它
把 a.py 里的 print("Walk") 改成 print("walk"),重新执行 python3 a.py < in.txt > out.txt
diff out.txt expected.txt 的输出:
1c1
< walk
> Walk
含义:第 1 行不同;< 开头是你的输出,> 开头是期望输出——大小写差一个字母(diff 还可能夹一行 --- 分隔符;上面三行是关键行)
改回 print("Walk"),再跑一次:diff 没有输出 → 一致 → 回到题目页重新提交,查看新一次记录| 现象 | 原因 | 处理 |
|---|---|---|
python3: command not found(或 python 不是内部或外部命令) | 没有安装 Python,或安装后命令名不同 | macOS 试 python3,Windows 试 python 或 py;都没有就先安装 Python 3 |
No such file or directory: a.py(或 in.txt) | 终端不在存放文件的目录 | 回到步骤 4,确认 cd 进了那个文件夹;用 ls(Windows 用 dir)看得到三个文件才对 |
| 运行后光标一直闪、没有任何输出 | 程序在等待你手动输入——命令里漏了 < in.txt | 按 Ctrl+C 结束,把重定向补上再运行 |
08 / 边界、反例与复杂度
四种错误做法在具体输入上各输出什么
下面每一行都是一个具体的错误程序:给出输入、错误输出与正确输出,你可以把参考程序改成对应写法自己复现。
| 错误做法 | 输入 | 错误输出 / 结果 | 正确输出 | 判题结果 |
|---|---|---|---|---|
| P2480 漏乘 1000(直接用公里除以米/分钟) | 1 2 100 1 50 | 计程车 1÷100+1 = 1.01,步行 2÷50 = 0.04 → 输出 Walk | 计程车 1000÷100+1 = 11,步行 2000÷50 = 40 → Taxi | 答案错误(WA) |
P2480 固定文本大小写错(taxi) | 1 2 100 1 50 | taxi | Taxi | 答案错误(WA) |
输出前先打印提示语 print("请输入:") | 任何输入 | 第一行多出 请输入: | 只有答案 | 答案错误(WA) |
P2484 用 input() 读了第三次(输入只有两行) | Hello World / l | 抛出读到文件尾异常(EOFError) | 3 | 运行错误(RE) |
P2484 目标是字母时只数小写(s.count(target.lower())) | Abca / a | 1(只数到小写 a,漏了大写 A) | 2 | 答案错误(WA) |
P2485 只在中间加 /,不去重 | /abc/,/bcd | /abc///bcd | /abc/bcd | 答案错误(WA) |
| 题目 | 输入规模 | 参考程序的时间与空间 | 结论 |
|---|---|---|---|
| P2480 | 固定 5 个整数 | O(1) 时间;O(1) 空间。交叉相乘比较要求 M、N 为正(速度),题目语义如此 | 按此量级不会接近时限;C++/Java 里 1000·X·N 这类乘积用 64 位整数存放 |
| P2484 | 字符串长度 n(以题目页为准) | O(n) 时间:count 扫描一遍或两遍;O(n) 空间存字符串,计数只用常数额外空间 | 一趟扫描 |
| P2485 | 两段各自长度小于 100 | O(L) 时间;O(L) 空间(切分出的片段与新字符串) | 一趟扫描 |
以上是按参考算法的量级估算,不是线上时限的实测;三道题的量级都远小于时限。本课练的是把「数据范围 → 可行复杂度」当成读题的固定动作(第 04 节的对照表),后面的课会用到。多组 A+B(练习 5)先收集答案再输出,空间是 O(n)。
09 / 渐进练习与参考答案
跟做 → 改一个条件 → 独立实现 → 迁移
每题先在纸上或文件里做完,再展开答案。答案给出计算过程或代码,以及做错时最可能错在哪一步。
练习 1(跟做):不运行程序,写出对输入 10 20⏎30⏎ 分别用读法 1(整行读一次)和读法 2(一次读完按空白切)得到的值,再写出把读法 2 的结果全部转成整数的一行代码。
展开练习 1 答案
读法 1 得到字符串 "10 20"(去掉了行尾换行);读法 2 得到 [b"10", b"20", b"30"]。转整数:nums = list(map(int, sys.stdin.buffer.read().split())) 得到 [10, 20, 30]。常见错误:以为读法 1 会得到两个数——它得到的是一整行文本,还要再 split()。
练习 2(改一个条件):P2480 的输入改成 2 1 1000 1 200,不运行程序,用整数交叉相乘算出输出。
展开练习 2 答案
计程车 = 1000×2×200 + 1×1000×200 = 400 000 + 200 000 = 600 000;步行 = 1000×1×1000 = 1 000 000。计程车更小 → Taxi。按分钟核对:计程车 2000÷1000 + 1 = 3 分钟,步行 1000÷200 = 5 分钟,一致。做错最常见的原因:等待时间 L 忘了也乘 M×N(它是加在分钟上的,同乘之后是 L×M×N)。
练习 3(改一个条件):P2485 的四种斜杠组合 /abc,bcd、/abc/,/bcd、/abc/,bcd、/abc,/bcd,写出正确输出;再写出「只在中间加一个 /、不做任何去重」的错误程序对这四组各输出什么。
展开练习 3 答案
| 输入 | 正确输出 | 只加 / 不去重的输出 |
|---|---|---|
/abc,bcd | /abc/bcd | /abc/bcd(碰巧对) |
/abc/,/bcd | /abc/bcd | /abc///bcd |
/abc/,bcd | /abc/bcd | /abc//bcd |
/abc,/bcd | /abc/bcd | /abc//bcd |
只有「都无」那一组碰巧正确。题目页示例 /abc/,/bcd 属于「都有」,能抓到这个错误(输出 /abc///bcd);只用自拟的「都无」输入自测才会漏掉它。仍应把四种组合都测一遍:例如「只去前缀尾部斜杠、不处理后缀开头」的写法(prefix.rstrip("/") + "/" + suffix)在「都无」「只前有」两组正确,在「都有」「只后有」两组都输出 /abc//bcd——题目页示例能抓到它,但只用「只前有」的自拟输入就会漏掉。
练习 4(独立实现):完成必做任务「代码自测」的两个解析函数并让断言通过;然后回答:parse_lines("a b\n") 的结果里,两个空格保留了吗?
展开练习 4 答案
两个解析函数的参考实现(自带断言)
Pythondef parse_tokens(text):
return [int(x) for x in text.split()]
def parse_lines(text):
return text.splitlines()
assert parse_tokens("3 5\n7\n") == [3, 5, 7]
assert parse_lines("hello world\nfoo bar\n") == ["hello world", "foo bar"]
# 接到标准输入:import sys; nums = parse_tokens(sys.stdin.read())splitlines() 按行切并去掉每行的换行,行内空格原样保留,所以结果是 ["a b"],两个空格都在。用 split("\n") 也可以,但输入末尾有换行时它会多出一个空串元素。
练习 5(迁移):写一个程序,第一行是组数 n,接下来 n 行每行两个整数,输出每组的和、每组一行——这就是基础加练里 A1002「A+B 问题 III」的形式。用输入 3⏎1 2⏎10 20⏎-5 5⏎ 自测,期望输出 3⏎30⏎0⏎。
展开练习 5 答案
多组 A+B 的完整程序(读法 3)
Pythonimport sys
data = sys.stdin.buffer.read().split() # 一次读完,按空白切成记号
it = iter(data)
n = int(next(it)) # 第一个记号是组数
answers = []
for _ in range(n):
a, b = int(next(it)), int(next(it))
answers.append(a + b)
print("\n".join(map(str, answers))) # 每组答案一行,最后一行也带换行这份程序对「n 组数据分别在 n 行」和「全部挤在一行」两种排版都正确,因为读法 3 不依赖换行位置。常见错误:在循环里逐次 input() 而 n 很大时变慢;或者忘了把 n 本身当作第一个记号取走。
10 / 读题要求、题解入口与复习自评
三道题的输入输出对照,以及完成本课之后怎么复习
提交前把下表过一遍。题目页的题面与样例是最终依据;本表按题目页现有说明整理,P2484 的大小写规则请以题目页那一句为准。
| 项目 | P2480 最快到达医院 | P2484 计算某个字符出现次数 | P2485 拼接 URL |
|---|---|---|---|
| 输入 | 一行五个整数 X Y M L N | 两行:字符串、目标字符 | 一行:前缀,后缀(逗号分隔,题目页:各自长度小于 100) |
| 读法 | 读法 2(按空白切) | 读法 1(整行读两次) | 读法 1(整行读一次再按逗号切) |
| 计算 | 单位统一 → 两条路线耗时 → 三分支比较 | 字母:大小写都计;非字母:精确计数 | 切片段 → 去空 → 用 / 连接并以 / 开头 |
| 输出 | Taxi / Walk / Same 之一 | 一个整数 | 一个字符串 |
| 样例 | 50 5 500 30 90 → Walk | 题目页为准(自拟:Hello World / l → 3) | /abc/,/bcd → /abc/bcd |
| 易错 | 漏乘 1000;Same 分支;大小写 | 第二行用 strip();只数一种大小写 | 只加 / 不去重;空段 |
题解入口:需要对照解法时,先展开本课第 06 节的三份完整参考程序;三道题的题目页另有思路与 Python / Java / C++ 参考代码,可在题目页查看。基础加练:本页下方「基础加练(选做)」的 5 道题(00000 第一次提交、A1000 / A1001 / A1002 三个 A+B、H100001 两数之和)从「原样输出」练到「哈希查找」,40 分钟内全部独立通过说明基础足够;用时更长就把学习周期适当拉长、把每课练扎实。
复习与自评:本课算完成 = 两道必做题 P2480、P2484 都通过判题,并勾选全部六条「学习完成检查」;进阶练习、基础加练与复习题不影响完成状态。六条检查是自评,勾选表示你认为自己能做到,它不会改变题目的通过(AC)状态。复习时用三个问题自测:① 不看正文,写出四种读法各适用什么输入;② 不看表格,重算 P2480 样例的两个交叉相乘值(5 850 000 与 2 500 000);③ 说出六种提交结果分别发生在判题四步的哪一步。答不出哪一条,就回到对应的节重读,再做第 09 节对应的练习。
基础加练(选做)
同一主题的 5 道基础题
这组题目采用标准输入输出(ACM 模式):程序从标准输入读取数据、把结果写到标准输出。它们难度低于必做题,适合在必做题之前热身,或在未通过时回来巩固;不计入本课完成标准。每道题给出练习重点、完成标准与提示;已在本站通过的题目会直接显示为已通过。
- A1002未开始
A+B 问题 III
多组测试 · 预计 12 分钟
- 练习重点:
- 循环、答案收集、批量输出
- 完成标准:
- 每组答案独占一行,组数与输出数一致
提示
先读数据组数,再循环处理;可以先收集答案,最后统一输出。
11 / 练习
按顺序完成本课的任务
必做题已通过 0/2 道;进阶练习已通过 0/1 道
写两个解析函数:按空白切分版和整行版
代码自测自主练习练习重点:同一段输入文本,练习「按空白切」和「按行切」两种解析;预计用时:15 分钟
完成标准:能说出什么时候必须整行读、什么时候一次读完再拆
需要时查看提示
按空白拆分版(parse_tokens)用 text.split(不带参数就按任意空白拆分)再逐个 int;整行版(parse_lines)用按行拆分函数(splitlines),行内空格原样保留。断言通过后再接标准输入(sys.stdin)。参考实现在第 09 节练习 4 的展开区。
自测代码(复制到你的代码文件中运行,检查输出是否一致)
import io
def parse_tokens(text):
# 请在这里实现:把整段输入按空白切成整数列表
...
def parse_lines(text):
# 请在这里实现:按行切分,保留行内空格,去掉行尾换行
...
assert parse_tokens("3 5\n7\n") == [3, 5, 7]
assert parse_lines("hello world\nfoo bar\n") == ["hello world", "foo bar"]
# 两个断言都通过后,再把同样的逻辑接到 sys.stdin 上P2480 · 最快到达医院的方法
必做任务 1练习重点:五个量按顺序读入;公里换米后再比时间;预计用时:20 分钟
完成标准:能口述两条路线的耗时公式,单位全部统一到米和分钟
需要时查看提示
医院距离是公里、速度是米/分钟——先把 X、Y 乘 1000。计程车耗时 = X×1000/M + 等待 L,步行耗时 = Y×1000/N。比较结果按题目指定的输出文本原样打印(大小写、标点完全一致),相等的情况按题目说明的规定输出。第 05 节给了两组完整手算。
P2484 · 计算某个字符出现次数
必做任务 2练习重点:整行读取字符串;目标为字母时按题目说明的大小写规则分类处理;预计用时:20 分钟
完成标准:能说清目标是字母和不是字母时,计数规则分别是什么
需要时查看提示
字符串可能含空格,用整行读法。先判断目标字符是不是字母:是字母时按题目规定处理大小写(逐字读题目说明中的那一句,不凭印象;题目页参考题解按「大小写都计入」实现),不是字母就直接数。Python 的计数方法(str.count)够用,关键在把规则读准。
P2485 · 拼接 URL
进阶练习 1进阶练习练习重点:逗号切分前后缀;结尾/开头的斜杠四种组合归一成一个;预计用时:15 分钟
完成标准:四种斜杠组合(都无、都有、只前有、只后有)都能写对
需要时查看提示
把两段都按 / 切成片段、丢掉空片段、用一个 / 连接并以 / 开头——四种组合就统一成一种写法。输入按逗号 split 成两段,注意段里可能是空串或只有一个 /。练习 3 的表给了四种组合的正确与错误输出。
提交结果
提交结果说明与处理方法
- CE
编译错误
Python 里多是语法或缩进错误;本地先跑一遍样例再提交
- WA
答案错误
优先检查三项:单位换算(公里是否乘 1000)、固定输出文本的大小写是否与题目一致、输出里是否混入提示语——第 08 节的表给出了每种错误的具体输出
- PE
格式错误
数字都对,空格或换行不对;对照样例逐字符检查行尾
- RE
运行错误
多数情况是读入越过末尾:input() 次数比输入行数多会抛出读到文件尾异常(EOFError)
- TLE
超时
本课的题都是一趟扫描,超时先查是不是把 input() 写进了大循环
- AC
通过
记下这题的读法(整行还是按空白切分)和你第一次出错的原因
12 / 学习完成检查
本课学习完成检查
完成本课需要:必做题全部通过,并勾选本课的全部学习完成检查;进阶练习、基础加练与复习题单独统计,不影响完成状态。登录后,勾选记录会保存到账号,并更新课程总览的完成状态。