01 / 本课学习路线
本课学习路线
阅读与推演约 113 分钟,练习约 55 分钟,进阶练习另需约 35 分钟
02 / 学习目标与先修自测
学完本课你能做什么,以及开始前需要会什么
动手前估算整数范围,写清精度和非法输入要求;提交前按清单核对。
| 学完后能做的事 | 正文位置 | 检查方式 |
|---|---|---|
| 动手前写出题目要求清单(类型 / 范围 / 精度 / 非法分支 / 批次形态),提交前逐条核对 | 第 03 节 | 自查第 2 条 |
| 口算判断一个乘法或累加会不会超 32 位有符号整数 | 第 04 节 | 自查第 3 条、练习 1 |
| 按题目要求的位数格式化浮点数,并知道 .xx5 临界值的真实舍入结果 | 第 05 节 | 自查第 4 条、练习 3 |
| 写全合法性检查并原样输出指定字符串,通过(AC)P2483、P2508 | 第 06、07 节 | 必做任务 1、2 |
| 把「内容」与「排版」分两步生成,做十进制到 m 进制的短除法 | 第 08 节 | 进阶练习 1、2 |
先修自测:下面 5 题请先自己写答案,再展开对照。答不出的按括号里的位置补看再回来。本课假定你已完成上一课(会整行读、按空白切、用 print 输出)。
展开先修自测答案
自测 1:得到 ["1", "", "0"],3 个元素——连续两个分隔符之间是一个空串。这就是 P2508 里「段数对但有空段」必须单独判掉的原因。
自测 2:True、False、False。isdigit() 对空串返回 False,所以「每段都是纯数字」的检查能顺带挡住空段。
自测 3:3.50;print(7 / 2) 输出 3.5,位数不可控。题目要求两位小数时必须格式化。
自测 4:2²⁴ = 16,777,216;2³¹−1 = 2,147,483,647(记作约 2.147×10⁹)。
自测 5:余数 1,商 3。短除法进制转换就是反复做这两步。
03 / 三类要求与题目要求清单
溢出、精度、非法分支:各有一个固定动作
同样的算法,换一份输入输出要求就可能全错。三件事各有一个固定动作:估上界、格式化、从题目说明中准确记录固定输出文本。
| 术语 | 含义 | 本课怎么处理 |
|---|---|---|
| 32 位有符号整数 | 取值范围 −2³¹ ~ 2³¹−1,即 −2,147,483,648 ~ 2,147,483,647;C++ 的 int、Java 的 int | 先估上界,可能超就用 64 位 |
| 64 位有符号整数 | 上限约 9.22×10¹⁸;C++ long long、Java long | 乘法、累加的默认选择 |
| Python 整数 | 不限宽度,不会溢出 | 估上界仍要做,为换语言留习惯 |
| 浮点格式化 | 按指定位数把小数转成文本,如 f"{x:.2f}" | 题目说几位就格式化成几位 |
| 舍入中点 | 恰好落在两个候选值正中间的数,如 0.875 对两位小数 | 格式化按二进制精确值判断,见第 05 节 |
| 负零 | 绝对值很小的负数格式化后显示为 -0.00 | 只有题目明确要求时才归一成 0.00 |
| 非法分支 | 输入不满足题目规定格式时的处理 | 输出题目指定的固定字符串,逐字符一致 |
| 批次形态 | 多组数据的组织方式:首行给组数 / 空行分组 / 读到文件尾 | 先数清输入有几个量、分几行、有无批次 |
题目要求清单模板(每题动手前复制一份到草稿或注释里)
① 输入:几个量、几行、有没有批次(首行组数 / 空行分组 / 读到文件尾) ② 类型与范围:每个量的最大值;乘法/累加的最坏上界 → 32 位够不够 ③ 输出精度:整数还是小数;保留几位;是否要求处理负零 ④ 非法分支:什么算非法;非法时输出的固定字符串(连引号原样抄) ⑤ 固定文本:所有要原样输出的词(大小写、标点、空格) 提交前:逐条打勾;没打勾的那条就是最可能出错的地方
非法分支的固定输出文本:记录与核对
题目要求输出 ERROR 就输出 ERROR;如果输出为其他写法(error、Error),与要求不一致,判题结果将为答案错误(WA)。记录题目要求时把指定字符串连引号原样写进注释,写代码时从注释复制,并核对大小写和标点。P2508 的固定字符串是 invalid IP——小写的 invalid、大写的 IP、中间一个空格。
补充学习(选学)多行与批次输入:先数清楚再动手约 4 分钟组数在第一行、空行分组、读到文件尾三种形态
批次输入常见三种形态:第一行给组数 n(最常见,循环 n 次);空行分隔批次(按行读,空行切段);不给组数读到文件尾(for line in sys.stdin)。题目要求清单第一条就是数清「输入有几个量、分几行、有没有批次」——以上一课的 P2480 为例,五个量少读一个,后续数据全部错位,所以应先核对输入项数量和分行方式。
04 / 整数宽度
上界估算与类型选择
拿到数据范围先做一次最坏情况的乘法或累加:把每个量代入它的最大值,算出结果最大能到多少,再和 2,147,483,647 比。
| 表达式 | 结果 | 对 2³¹−1 = 2,147,483,647 |
|---|---|---|
| 46340 × 46340 | 2,147,395,600 | 不超(恰好压线) |
| 46341 × 46341 | 2,147,488,281 | 超——int 直接溢出 |
| 50000 × 50000 | 2,500,000,000 | 超 |
| 2 × 10⁹ | 2,000,000,000 | 不超(但裕量只剩约 7%) |
| 128×2²⁴ + 255×2¹⁶ + 255×2⁸ + 255 | 2,164,260,863 | 超——这是 P2508 合法输入的最大结果 |
结论只有一条:乘法、累加先估最坏上界,可能超就直接用 64 位。2×10⁹ 这类「差一点超」的值最容易出问题,累加两次就超出范围。P2508 在 Python 里不用管;用 C++/Java 做时结果必须放进 64 位(或无符号 32 位)类型。
估算方法:① 找出结果由哪些量相乘或相加得到;② 每个量取题目给的最大值;③ 把结果与 2³¹−1 = 2,147,483,647 比(负数与 −2³¹ 比)——这是唯一的判定标准;46340 只是「平方不超」的临界:|n| ≤ 46340 时 n² 一定不超,但 46340×46341 = 2,147,441,940 比 46340² 大却仍不超,所以非平方的乘积不能套这个口诀;④ 累加时用「单项最大值 × 项数」。Python 整数不限宽,但这一步照做:模拟考试允许换语言,且 C++/Java 的溢出不报错、只给一个错误的数,最难查。
05 / 浮点打印
位数格式化与两位小数的真实舍入结果
题目要求两位小数就用 f"{x:.2f}";直接 print 一个浮点数位数不可控。格式化的舍入不是「四舍五入」四个字能概括的,下表用 6 个数给出真实结果。
| 数值 | f"{x:.2f}" 的输出 | 原因 |
|---|---|---|
| 0.875 | 0.88 | 0.875 能被二进制精确表示,正好是中点,取偶数位(8) |
| 0.125 | 0.12 | 同样是精确中点,取偶数位(2) |
| 2.675 | 2.67 | 2.675 在二进制里存成 2.67499999…,低于中点 |
| 1.005 | 1.00 | 存成 1.00499999…,低于中点 |
| 5 / 2 | 2.50 | 不足位数补 0 |
| -0.001 | -0.00 | 负零:绝对值小于 0.005 的负数格式化后带负号 |
规则:格式化按数值在二进制里的精确值判断;恰好是中点时取偶数位。遇到 .xx5 临界值的题,先用第 06 节的检查脚本跑一遍你关心的数,再决定是否按题目定义自己实现舍入。绝对值很小的负数会显示 -0.00,只有题目明确要求避免负零时才处理。
P2483「单词重量」用到的就是这一条:一行若干单词,按题目页的重量定义计算后保留两位小数(题目页参考题解的定义是所有单词的平均长度)。例如 hello world → (5+5)÷2 = 5.00;a bb ccc → (1+2+3)÷3 = 2.00;ab cde → 5÷2 = 2.50;aa aa aa aa aa aa aa a → 15÷8 = 1.875 → 1.88(二进制精确中点,取偶数位)。每个单词至少 1 个字母,所以平均长度不可能小于 1。数值算对但没格式化(输出 5.0、2.5)同样不通过。
补充学习(选学)浮点打印的三个细节约 5 分钟格式化位数、负零的处理、临界值舍入验证
第一,位数用格式化控制:f"{x:.2f}" 保留两位,直接 print 一个 float 位数不可控。第二,绝对值很小的负数格式化后可能输出 -0.00;仅当题目明确要求避免负零时,再按指定精度和舍入规则处理,否则保留正常的格式化结果。第三,遇到 .xx5 的临界值各语言舍入行为不完全一致,遇到就构造一个小样例先验证,必要时按题目定义自己实现舍入。
06 / 完整手算例
P2508:从字符串 128#0#255#255 到整数 2,147,549,183
题目:一种虚拟 IPv4 地址由四小节组成、以 # 分隔,第一小节范围 1~128,其余三节 0~255;把它转换成 32 位整数,非法则输出固定字符串 invalid IP。题目页给了两个例子:128#0#255#255 → 2147549183,1#0#0#0 → 16777216。
手算:128#0#255#255 拼成 32 位整数
切分: "128#0#255#255".split("#") → ["128", "0", "255", "255"],恰好 4 段,都是纯数字
段检查: 128∈[1,128] ✓ 0∈[0,255] ✓ 255 ✓ 255 ✓
每段占 8 个二进制位,从高到低拼接:
128×2²⁴ = 2,147,483,648
0×2¹⁶ = 0
255×2⁸ = 65,280
255×2⁰ = 255
合计 = 2,147,549,183 ← 程序输出必须和它逐字节一致(题目页示例)| 输入 | 四段 | 拼接 | 输出 |
|---|---|---|---|
| 1#0#0#0 | [1, 0, 0, 0] | 1×2²⁴ = 16,777,216 | 16777216(题目页示例) |
| 128#255#255#255 | [128, 255, 255, 255] | 2,147,483,648 + 16,711,680 + 65,280 + 255 | 2164260863(超出 32 位有符号上限,见第 04 节) |
| 步骤 | 代码 | 说明 |
|---|---|---|
| 整行读入并切分 | parts = s.split("#") | 分隔符是 #,不是点号;空段会切出空串 |
| 段数检查 | len(parts) != 4 | 3 段或 5 段都非法 |
| 纯数字检查 | all(p.isdigit() for p in parts) | 字母、符号、空串都非法 |
| 范围检查 | 1 <= a <= 128 与 0 <= x <= 255 | 第一段 1~128(0 非法),其余 0~255 |
| 拼接 | a * 2**24 + b * 2**16 + c * 2**8 + d | 与手算的四项相加一一对应 |
| 非法输出 | print("invalid IP") | 固定文本从题目说明原样复制 |
展开完整参考程序:P2508 IPv4 地址转整数(先自己写完并提交一次,再展开对照)
完整程序:P2508(标准输入 → 标准输出)
Pythonimport sys
s = sys.stdin.readline().rstrip("\n")
parts = s.split("#") # 题目用 # 分隔四个小节
def valid(parts):
if len(parts) != 4: # 必须恰好四段
return False
if not all(p.isdigit() for p in parts): # 每段必须是纯数字(空串也不行)
return False
a, b, c, d = (int(p) for p in parts)
if not 1 <= a <= 128: # 第一段 1~128,不是 0~255
return False
return all(0 <= x <= 255 for x in (b, c, d))
if valid(parts):
a, b, c, d = (int(p) for p in parts)
print(a * 2**24 + b * 2**16 + c * 2**8 + d)
else:
print("invalid IP") # 固定文本,从题目说明原样复制自测用例:题目页两个示例、合法最大值 128#255#255#255 → 2164260863,以及第 07 节的 7 种非法输入。题目页参考题解用十六进制拼接,本程序用乘 2 的幂,结果相同。
先检查再拼接的顺序不能反:int(p) 对空串或字母会抛出 ValueError(运行错误),所以必须在 isdigit 通过之后才转整数。
检查脚本:两位小数格式化的真实结果(自带断言)
Python# 两位小数格式化的实际行为:先在纸上写下你的预期,再运行核对
cases = [0.875, 2.675, 1.005, 0.125, 5 / 2, 7 / 8]
for x in cases:
print(repr(x), "->", f"{x:.2f}")
# 期望输出:
# 0.875 -> 0.88 0.875 能被二进制精确表示,正好是中点,取偶数位 → 0.88
# 2.675 -> 2.67 2.675 存成 2.67499999…,低于中点 → 2.67
# 1.005 -> 1.00 同理,1.005 存成 1.00499999…
# 0.125 -> 0.12 精确中点,取偶数位 → 0.12
# 2.5 -> 2.50
# 0.875 -> 0.88
assert f"{0.875:.2f}" == "0.88" and f"{2.675:.2f}" == "2.67" and f"{0.125:.2f}" == "0.12"
assert f"{-0.001:.2f}" == "-0.00" # 负零:只有题目明确要求时才处理第 05 节表格里的数都由这份脚本复现。把你关心的数加进 cases 再运行,就能看到它的真实格式化结果。
07 / 非法分支
P2508 的七种非法输入各输出什么,以及 P2483 的完整程序
非法分支只有一个正确输出:题目指定的固定字符串。下表把常见非法输入逐个列出,每一行都可以直接喂给你的程序核对。
| 输入 | 违反的规则 | 正确输出 |
|---|---|---|
| 0#1#1#1 | 第一段必须 1~128(0 非法) | invalid IP |
| 129#0#0#0 | 第一段超过 128 | invalid IP |
| 1#256#0#0 | 其余段超过 255 | invalid IP |
| 1#0#0 | 只有 3 段 | invalid IP |
| 1#0#0#0#0 | 有 5 段 | invalid IP |
| a#0#0#0 | 含非数字字符 | invalid IP |
| 1##0#0 | 有空段(切分后是空串) | invalid IP |
题目页说明「不用考虑其它非法情况」以外的输入以题目页为准;这七种都由第 06 节程序的四个检查覆盖。带前导零的段(如 01)在参考题解里按数字 1 处理,本程序与其一致。
展开完整参考程序:P2483 单词重量
完整程序:P2483(标准输入 → 标准输出)
Pythonimport sys
words = sys.stdin.readline().split() # 一行若干单词,按空白切开
total = sum(len(w) for w in words) # 总长度
average = total / len(words) # 平均单词重量(真除法,得到浮点数)
print(f"{average:.2f}") # 保留两位小数重量定义以题目页为准(参考题解:所有单词长度之和 ÷ 单词个数);输出两位小数。自拟输入 hello world → 5.00,ab cde → 2.50。P2483 的题面与样例在题目页查看;本课的输入为自拟。
08 / 进阶两题
排版输出(P2513)与进制转换(P2531)的手算与程序
两道进阶题各练一个固定动作:格式题把「内容」和「排版」分成两步;进制题用短除法逐位取余。
P2513「数字反转打印」:第 i 行放 i 个数,数字从 1 起依次递增;奇数行正序、偶数行逆序;每个数字占 4 位不足补 *;数字之间 4 个空格;最后一行顶格,第 n−1 行相对第 n 行缩进 4 个空格。先算每行的数字,再统一排版。
手算 n=3:先内容,后排版(␣ 表示一个空格)
第 1 行数字 [1] 奇数行正序 → [1] 第 2 行数字 [2, 3] 偶数行逆序 → [3, 2] 第 3 行数字 [4, 5, 6] 奇数行正序 → [4, 5, 6] 每个数补成 4 位:1*** / 3*** 2*** / 4*** 5*** 6***;数字间 4 个空格 缩进:第 i 行前放 (n−i)×4 个空格 → 第 1 行 8 个,第 2 行 4 个,第 3 行 0 个 输出: ␣␣␣␣␣␣␣␣1*** ␣␣␣␣3***␣␣␣␣2*** 4***␣␣␣␣5***␣␣␣␣6*** 题目页 n=5 的示例末行是 11**␣␣␣␣12**␣␣␣␣13**␣␣␣␣14**␣␣␣␣15**:两位数补两个 *
展开完整参考程序:P2513 数字反转打印
完整程序:P2513(标准输入 → 标准输出)
Pythonimport sys
n = int(sys.stdin.readline())
def cell(x):
return str(x).ljust(4, "*") # 每个数字占 4 位,不足补 *
for i in range(1, n + 1): # 第 i 行有 i 个数
first = i * (i - 1) // 2 + 1 # 本行最小的数
last = first + i - 1 # 本行最大的数
nums = list(range(first, last + 1))
if i % 2 == 0: # 偶数行逆序
nums.reverse()
indent = " " * 4 * (n - i) # 第 i 行相对末行缩进 (n-i)×4 个空格
print(indent + (" " * 4).join(cell(x) for x in nums))自测用例:题目页 n=5 的示例(逐字节核对,含每行前导空格)与上面手算的 n=3。第 i 行的第一个数是 i(i−1)/2 + 1(前 i−1 行共放了 1+2+…+(i−1) 个数),最后一个数是它加 i−1。
P2531「来自异国的客人」:输入 k n m,把十进制的 k 写成 m 进制,数其中数字 n 出现的次数。题目页示例:10 2 4 → 2,因为 10 = 2×4 + 2 = 四进制的 22。短除法:10 ÷ 4 = 2 余 2(最低位是 2),2 ÷ 4 = 0 余 2(次低位是 2),商为 0 停止;两位都是 2,答案 2。数个数不需要把各位反转成正序。
| 输入 k n m | 逐步取余 | 各位(低→高) | 输出 |
|---|---|---|---|
| 10 2 4 | 10%4=2, 10//4=2;2%4=2, 2//4=0 | [2, 2] | 2(题目页示例) |
| 255 15 16 | 255%16=15, 255//16=15;15%16=15, 15//16=0 | [15, 15] | 2(m>10 时一位数字可以是两位十进制数,所以逐位比较整数,不拼字符串) |
| 0 0 2 | k=0,循环一次都不进 | [0] | 1(0 本身是一位数字 0,要单独处理) |
展开完整参考程序:P2531 来自异国的客人
完整程序:P2531(标准输入 → 标准输出)
Pythonimport sys
k, n, m = map(int, sys.stdin.readline().split())
count = 0
if k == 0: # 0 在任何进制下都是一位数字 0
count = 1 if n == 0 else 0
while k > 0:
digit = k % m # 最低位
if digit == n:
count += 1
k //= m # 去掉最低位
print(count)题目保证 n < m,所以幸运数字一定是 m 进制下合法的一位。k=0 单独处理,否则 while 循环一轮都不执行、漏数那一个 0。
09 / 边界、反例与复杂度
错误做法在具体输入上各输出什么
下面每一行都是一个具体的错误程序,给出输入、错误输出与正确输出;把参考程序改成对应写法就能复现。
| 错误做法 | 输入 | 错误输出 | 正确输出 | 判题结果 |
|---|---|---|---|---|
| P2508 第一段范围写成 0~255 | 0#1#1#1 | 65793(把它当合法地址拼了) | invalid IP | 答案错误(WA) |
P2508 固定文本写成 Invalid IP | 1#0#0 | Invalid IP | invalid IP | 答案错误(WA) |
P2508 不查段数就取 parts[3] | 1#0#0 | 下标越界异常(IndexError) | invalid IP | 运行错误(RE) |
P2508 先 int() 再检查 | a#0#0#0 | 转换异常(ValueError) | invalid IP | 运行错误(RE) |
P2483 直接 print(average) | hello world | 5.0 | 5.00 | 答案错误(WA)或格式错误(PE),取决于判题器 |
| P2513 数字间用 1 个空格 | 3 | 第 3 行 4*** 5*** 6*** | 第 3 行 4***␣␣␣␣5***␣␣␣␣6*** | 答案错误(WA) |
| P2531 把各位拼成十进制字符串再数子串 | 241 1 16 | 各位从高到低是 [15, 1],拼成 "151" 后数字符 "1" 得 2(低位在前直接拼是 "115",同样得 2) | 1(进制数字 1 只有一位) | 答案错误(WA) |
第一行的错误输出计算:0×2²⁴ + 1×2¹⁶ + 1×2⁸ + 1 = 65,536 + 256 + 1 = 65,793。
| 题目 | 输入规模 | 参考程序的复杂度 | 结论 |
|---|---|---|---|
| P2483 | 一行单词(长度以题目页为准) | O(L):切分与求和各扫一遍 | 一趟扫描 |
| P2508 | 一个短字符串 | O(L):切分 + 常数次检查与乘加 | 常数级 |
| P2513 | 1 ≤ N ≤ 30 | O(N²):共 N(N+1)/2 个数字 | 最多 465 个数字 |
| P2531 | k 的位数约 log_m k | O(log k):每位一次取余整除 | 常数级 |
四道题按量级估算都远小于时限;若超时(TLE),先查读入写法(是否把 input() 写进大循环),再查算法。
10 / 渐进练习与参考答案
跟做 → 改一个条件 → 独立实现 → 迁移
每题先在纸上或文件里做完,再展开答案。
练习 1(跟做):不用计算器,判断下面 6 个值是否在 32 位有符号整数范围内:46340²、46341²、50000²、2×10⁹、2³¹、P2508 合法最大值 2,164,260,863。判断完再用「代码自测」任务的断言核对。
展开练习 1 答案
判断函数 fits_int32 的参考实现(自带断言)
PythonINT32_MAX = 2**31 - 1 # 2,147,483,647
def fits_int32(v):
return -2**31 <= v <= INT32_MAX
assert fits_int32(46340 * 46340) is True
assert fits_int32(46341 * 46341) is False
assert fits_int32(50000 * 50000) is False
assert fits_int32(2 * 10**9) is True
assert fits_int32(2**31) is False # 恰好超出 1
assert fits_int32(128 * 2**24 + 255 * 2**16 + 255 * 2**8 + 255) is False # P2508 的最大值 2,164,260,863
assert fits_int32(46340 * 46341) is True # 2,147,441,940:比 46340² 大,仍不超——口诀只对平方成立
assert fits_int32(-2**31) is True # 下界本身在范围内不超 / 超 / 超 / 不超 / 超(恰好多 1)/ 超 / 不超(46340×46341 比 46340² 大却仍在范围内)/ 不超(下界 −2³¹ 本身)。判断只和 2³¹−1、−2³¹ 比;「|n| ≤ 46340」只用于判断 n² 是否不超,不能推广到任意乘积。
练习 2(改一个条件):P2508 输入 100#200#0#1,先手算输出;再把第一段改成 0,说明输出为什么变了。
展开练习 2 答案
100×2²⁴ = 1,677,721,600;200×2¹⁶ = 13,107,200;0×2⁸ = 0;1 → 合计 1,690,828,801,输出 1690828801。第一段改成 0 后违反「第一段 1~128」,输出 invalid IP——不是 0 拼出来的 13,107,201。做错最常见的原因:把第一段范围记成 0~255。
练习 3(改一个条件):P2483 输入 abc de f 和 abcd efg,各输出什么?再构造一个平均长度恰为 1.875 的输入,说明为什么输出是 1.88 而不是 1.87。
展开练习 3 答案
abc de f → (3+2+1)÷3 = 2 → 2.00;abcd efg → (4+3)÷2 = 3.5 → 3.50。平均 1.875 = 15÷8:8 个单词总长 15,例如 aa aa aa aa aa aa aa a(七个双字母加一个单字母)→ 1.875 是二进制精确中点,格式化取偶数位得 1.88。注意每个单词至少 1 个字母,平均长度不可能小于 1——「总长 7、8 个单词」这种输入不存在。这一题的要点:先判断数值是不是精确中点,再决定舍入方向;不确定就跑第 06 节的检查脚本。
练习 4(独立实现):不看参考程序,写出 P2513 并用 n=3 自测,输出应与第 08 节的三行逐字符一致(包括第 1 行前面的 8 个空格);再用 n=5 与题目页示例比对。
展开练习 4 答案
参考程序在第 08 节的展开区。自测通不过时按三条规则逐条查:补 * 用 ljust(4, "*");数字间是 4 个空格;第 i 行前有 (n−i)×4 个空格且最后一行没有。用上一课的重定向比对(diff)能看出多一个或少一个空格。
练习 5(迁移):P2531 输入 241 15 16 与 241 1 16,各输出什么?(241 = 15×16 + 1)
展开练习 5 答案
241 ÷ 16 = 15 余 1 → 最低位 1;15 ÷ 16 = 0 余 15 → 次低位 15。各位是 [1, 15]。幸运数字 15 出现 1 次,幸运数字 1 出现 1 次,两问都输出 1。如果把各位拼成字符串 "151" 再数子串,数 "15" 得 1、数 "1" 得 2——第二问就错了,这就是第 09 节表最后一行说的按位比较整数的原因。
11 / 读题要求与复习自评
四道题的要求对照,以及完成本课之后怎么复习
提交前把下表过一遍;题目页的题面与样例是最终依据。P2483 的重量定义、P2513 的 N 范围以题目页为准。
| 项目 | P2483 单词重量 | P2508 IPv4 转整数 | P2513 数字反转打印 | P2531 来自异国的客人 |
|---|---|---|---|---|
| 输入 | 一行若干单词 | 一行,# 分隔的四段 | 一个整数 N(1~30) | 一行三个整数 k n m |
| 计算 | 平均长度(以题目页为准) | 四项检查 → 拼接 | 每行数字 → 补位 → 缩进 | 短除法逐位取余 |
| 输出精度 / 格式 | 两位小数 | 整数或固定串 invalid IP | 每行前导空格 + 4 空格间隔 + * 补位 | 一个整数,行末无空格 |
| 非法分支 | 无 | 段数 / 纯数字 / 第一段 1~128 / 其余 0~255 | 无 | 无(n < m 由题目保证) |
| 溢出提醒 | 无 | 结果最大 2,164,260,863,C++/Java 用 64 位 | 无 | 无 |
| 样例 | 题目页为准(自拟 hello world → 5.00) | 128#0#255#255 → 2147549183 | N=5 见题目页 | 10 2 4 → 2 |
题解入口:需要对照解法时,先展开本课第 06、07、08 节的完整参考程序;四道题的题目页另有思路与 Python / Java / C++ 参考代码,可在题目页查看。复习与自评:本课算完成 = 两道必做题 P2483、P2508 都通过判题,并勾选全部六条「学习完成检查」;进阶练习与复习题不影响完成状态。六条检查是自评,不改变题目的通过(AC)状态。复习时用三个问题自测:① 不看正文,默写题目要求清单的五条;② 不看表格,说出 0.875 和 2.675 各格式化成什么、为什么;③ 手算 1#0#0#0 的输出并列出 P2508 的四项检查。答不出哪一条,就回到对应的节重读,再做第 10 节对应的练习。
12 / 练习
按顺序完成本课的任务
必做题已通过 0/2 道;进阶练习已通过 0/2 道
实现 fits_int32:先在纸上判断,再用断言验证
代码自测自主练习练习重点:先判断四个表达式是否超出 32 位整数范围,再用判断函数(fits_int32)的断言核对结果;预计用时:10 分钟
完成标准:能不用计算器说出 46340² 不超 32 位上限、46341² 超出
需要时查看提示
记住一个参照值即可:2³¹−1 ≈ 2.147×10⁹,46340² = 2,147,395,600 恰好不超。遇到乘法或累加,先用题目给出的最大值算出上界,再判断 32 位还是 64 位整数足够。参考实现在第 10 节练习 1 的展开区。
自测代码(复制到你的代码文件中运行,检查输出是否一致)
def fits_int32(v):
# 请在这里实现:判断 v 能否放进 32 位有符号整数(上限 2**31 - 1 = 2,147,483,647)
...
# 先在纸上判断每一行是否超出范围,再运行断言——断言失败的那一行就是需要重新核对的范围边界
assert fits_int32(46340 * 46340) is True # 2,147,395,600 恰好压线
assert fits_int32(46341 * 46341) is False # 2,147,488,281 越线
assert fits_int32(50000 * 50000) is False # 2,500,000,000
assert fits_int32(2 * 10**9) is True # 2,000,000,000,裕量只剩约 7%P2483 · 单词重量
必做任务 1练习重点:按题目规则算重量;结果保留两位小数输出;预计用时:20 分钟
完成标准:能说出自己用的格式化写法,以及为什么不能直接 print
需要时查看提示
重量公式逐字按题目说明翻译,不要自行「优化」。结果用 f"{w:.2f}" 输出;先用样例验证位数和舍入方向,再提交。第 05 节的表给了两位小数格式化的真实结果。
P2508 · IPv4 地址转整数
必做任务 2练习重点:合法性检查(段数、每段范围、纯数字)+按位拼接;预计用时:25 分钟
完成标准:能列全合法性清单:四段、第一段 1~128、其余 0~255、非法输出指定串
需要时查看提示
先写检查再写拼接:按 # 拆分(split)后段数必须是 4,每段必须是纯数字,第一段范围 1~128(不是 0~255,这是本题最容易忽略的限制),其余 0~255。全部通过才计算 a×2²⁴+b×2¹⁶+c×2⁸+d;任何一条不满足,原样输出题目指定的字符串。第 07 节的表给了 7 种非法输入。
P2513 · 数字反转打印
进阶练习 1进阶练习练习重点:逐行构造输出:补位、缩进、奇正偶逆;预计用时:20 分钟
完成标准:每个数字占 4 位不足补 *、相邻 4 空格、末行顶格的三条规则都能对上样例
需要时查看提示
先把每行的数字序列生成出来(第 i 行 i 个,奇数行正序、偶数行逆序),再统一做格式化:每个数字用左对齐补齐方法(ljust)处理,写成 str(x).ljust(4, "*"),数字间 4 个空格,第 k 行相对末行缩进 (n−k)×4 个空格。格式题的要点是把「内容」和「排版」分成两步。第 08 节给了 n=3 的逐字符输出。
P2531 · 来自异国的客人
进阶练习 2进阶练习练习重点:十进制转 m 进制,统计幸运数字出现次数;预计用时:15 分钟
完成标准:能手算一个小例子:k=10、m=4 时四进制 22 里数字 2 出现 2 次
需要时查看提示
循环取余:k % m 是最低位,k //= m 进下一位,边取边计数,不需要真的拼出字符串。注意 k=0 时也要处理一位。第 08 节的表给了三组逐步取余。
提交结果
提交结果说明与处理方法
- WA
答案错误
优先检查四项:第一段范围是否写成 0~255、非法输出串的大小写、浮点位数是否按要求格式化、负零的处理是否符合题目要求——第 09 节的表给出了每种错误的具体输出
- PE
格式错误
P2513 这类排版题逐字符比对:补位用 *、间隔 4 空格、行末不能多空格
- RE
运行错误
split("#") 后没先验段数就取下标;纯数字校验漏了空串;先 int() 再检查
- TLE
超时
本课的题都是常数或 O(n) 扫描,超时先查读入写法
- AC
通过
把这题的要求清单存档:下次遇到同类题直接复用清单结构
13 / 学习完成检查
本课学习完成检查
完成本课需要:必做题全部通过,并勾选本课的全部学习完成检查;进阶练习、基础加练与复习题单独统计,不影响完成状态。登录后,勾选记录会保存到账号,并更新课程总览的完成状态。