拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

蓝桥杯Python算法模板:输入输出、数据结构与DP实战

蓝桥杯Python算法模板:输入输出、数据结构与DP实战

1. 赛前先想清楚:为什么 Python 选手更需要模板

打蓝桥杯的人里,用 Python 的那批心态其实挺微妙的。C++ 选手觉得自己慢工出细活,Java 选手觉得 API 齐全,Python 选手最大的底气就是写得快——但最大的焦虑也是"快得没边",快到最后忘了边界、忘了格式、忘了大数据量会不会 TLE。我自己从第一次参赛到现在,最大的感受就是:模板不是让你偷懒,是让你把注意力从"怎么写"挪到"怎么想"。

蓝桥杯官方给的 Python 环境和本地其实是有差异的,历年真题里像"蚂蚁感冒"这类模拟题、2022 国 B 的"出差"这类最短路题,考的都是能不能在有限时间里把标准解法敲对。而你一旦在赛场上从零手写并查集、从零搭 BFS 骨架,那几分钟的犹豫加上一个 lower_bound 的边界没抠准,基本就告别省一了。

我给自己定过一条纪律:凡是两周内写过两遍以上的代码结构,一律沉淀成模板。下面这套东西就是按这个标准攒出来的,覆盖输入输出、基础工具、数据结构、搜索图论、动态规划、数论六个方向,你可以直接拿去用,也可以按自己的书写习惯改。

1.1 蓝桥杯赛制对 Python 的友好与不友好

蓝桥杯软件类的题目分填空题和编程题,填空题的答案往往是一个数字或一个字符串,你就算用 Python 暴力枚举也能出结果,这类题其实是 Python 的主场——一行itertools.permutations就能省掉 C++ 选手半小时的递归。

不友好的地方在于运行时限和递归深度。Python 的常数因子大概是 C++ 的 30 到 50 倍,一个 1e8 级别的循环,C++ 一秒过,Python 得七八秒。所以模板里那些"能少一个循环就少一个循环"的写法不是炫技,是被逼出来的。另外 Python 默认递归深度上限是 1000,DFS 深搜稍微深一点就RecursionError,所以我在每个模板文件的头部都会加一句sys.setrecursionlimit(10**6),这一句能救回至少一道题。

还有一个很多人忽视的点:蓝桥杯是按测试点给分的,不是全对才有分。也就是说,哪怕你的正解只过了前 60% 的数据,你也是有分的。所以模板设计的第一原则不是"最优",而是**"能过就先用,时间够再优化"**。

1.2 模板要分成三层,别一股脑塞进一个文件

新手常见做法是把所有模板抄在一个template.py里,几百行,赛场上翻都翻不到。我的做法是分三层:

  • 一层是 IO 层:读输入的几种写法、快速输出,这几行必须闭着眼睛能敲。
  • 二层是工具层:排序、二分、前缀和、并查集、堆、Counter,这些是"随时可能用到"的通用件。
  • 三层是算法层:BFS/DFS、Dijkstra、背包、筛法,这类是"题目点出来了才用"的。

三层分开写在三个文件里,赛前各扫一遍,比赛时按需粘。这样做的好处是:你面对一道题时,脑子里第一个反应是"这题属于哪一层",而不是翻代码。

2. 输入输出模板:决定你能不能拿到第一分

我见过太多人算法想对了,最后栽在输入输出上。蓝桥杯的输入格式描述有时候写得比较含糊,"第一行输入两个整数 n 和 m"这种还好,怕的是"接下来若干行,每行若干个整数"——"若干"两个字就是坑,你得自己判断是读到文件结束还是读到 n 行为止。

我的经验是:先把输入完整读进来,再在内存里切分。这样你只需要维护一套解析逻辑,不用管是单行多行。核心就是sys.stdin.read().split()这一句,它把整个输入按空白字符切开,变成一个字符串列表,速度还比input()循环快好几倍。

2.1 三种输入形态的读法

第一种是固定行数、每行固定个数。这种最省事,用map(int, input().split())一行搞定:

import sys n, m = map(int, sys.stdin.readline().split()) a = list(map(int, sys.stdin.readline().split()))

第二种是第一行给行数,后面每行不定长。这时候别逐行input(),直接在读全量数据后按下标游标推进:

import sys def main(): data = sys.stdin.read().split() idx = 0 n = int(data[idx]); idx += 1 m = int(data[idx]); idx += 1 # 例如后面是一张 n 行 m 列的网格 grid = [] for i in range(n): row = data[idx: idx + m] idx += m grid.append(row) # ... 后续处理 main()

这种"游标推进"的写法我强烈推荐。原因很实在:当题目给的输入一部分是整数、一部分是字符(比如网格里混着.和#),你没法统一转 int,游标法是唯一能同时应付两种类型的方式。

第三种是读到 EOF 为止。蓝桥杯有些题不给数量,直接给一串数据。这种用for line in sys.stdin:最稳,注意每行末尾的换行符要strip()掉。

注意:sys.stdin.read()之后 stdin 就被读空了,所以整个程序里只能调用一次。如果你的逻辑要分函数处理,记得把 data 作为参数传下去,不要在函数里再 read 一遍。

2.2 输出格式的隐形陷阱

输出这块有三个高频扣分点,我按踩坑频率排个序:

陷阱具体表现规避写法
多余的换行循环里逐个print,最后多一个空行用' '.join(map(str, ans))一次输出
该换行没换行多组答案拼在一行每组答案单独sys.stdout.write(...+'\n')
浮点精度print(3.0)输出3.0,题目要的是3按题意决定是否int()或格式化

关于浮点,多说一句。蓝桥杯的填空题很多答案是"保留两位小数",这时候用print(f"{x:.2f}");但如果是编程题,通常是"输出一个整数",那你就得想清楚中间过程用不用浮点。我个人建议能用整数运算就别碰浮点,比如比较a/b > c/d,写成a*d > c*b,避开了精度问题还更快。

2.3 大数据量下的读写加速

这一条是 Python 选手的命根子。当数据量到 1e5、1e6 级别的时候,input()和print()的开销会变成主要耗时。标准做法是把input换成sys.stdin.readline,把print换成sys.stdout.write,然后在 main 里统一缓冲:

import sys input = sys.stdin.readline out = [] def main(): n = int(input()) for _ in range(n): v = int(input()) out.append(str(v * 2)) sys.stdout.write('\n'.join(out)) main()

为什么把结果先攒到 list 里再一次性写出,而不是边算边 write?因为单次write也有系统调用开销,攒起来一次写出,能把输出耗时压到原来的几分之一。这个技巧在我做的几道 1e5 规模的排序题上实测差别很明显,具体数字跟环境有关,但方向一定是对的。

3. 基础工具模板:排序、二分、前缀和与双指针

这一层是"用得最多、最不容易写错、但一旦错了最不容易发现"的部分。为什么这么说?因为它们的错误往往是边界差一位,而边界差一位在样例上跑得完全正常,只有大数据才会挂。所以这一层的模板,重点不是写法本身,而是边界的固定策略。

3.1 排序与自定义比较

Python 的sort是稳定的 Timsort,平均和最坏都是 O(n log n),直接放心用。真正常用的是自定义排序:元组排序天然按元素顺序比较,所以多关键字排序直接用元组就行,不需要写比较函数。

# 先按分数降序,分数相同按名字升序 people = [("bob", 90), ("alice", 95), ("carol", 90)] people.sort(key=lambda x: (-x[1], x[0]))

这里的关键技巧是**"降序用负号"**。很多人写reverse=True配上多关键字就懵了,因为reverse=True会把所有关键字一起翻转。用负号只翻转你想翻转的那一维,思路清晰还不容易错。注意这个技巧只对数值类型有效,字符串没法取负,字符串降序就只能老老实实排两遍或者用functools.cmp_to_key。

心得:cmp_to_key我一般只在真的没法转成元组比较时才用,因为它每次比较都要回调 Python 函数,速度比 key 排序慢不少。能转元组就转元组。

3.2 二分的三个模板与边界处理

二分的坑我总结成一句话:你写的是"找第一个满足条件的位置"还是"找最后一个满足条件的位置",这两件事必须一开始就想清楚。想清楚之后,后半段就是机械执行。下面是我固定的两个写法:

# 写法一:找第一个 >= x 的位置(左边界) def lower_bound(a, x): lo, hi = 0, len(a) # 答案在 [lo, hi] 闭区间 while lo < hi: mid = (lo + hi) // 2 if a[mid] >= x: hi = mid else: lo = mid + 1 return lo # 返回 len(a) 表示没找到 # 写法二:找最后一个 <= x 的位置(右边界) def upper_bound(a, x): lo, hi = -1, len(a) - 1 while lo < hi: mid = (lo + hi + 1) // 2 # 注意这里 +1 if a[mid] <= x: lo = mid else: hi = mid - 1 return lo # 返回 -1 表示没找到

第二个写法里的+1是最容易被漏的地方。为什么必须加?因为当区间只剩两个元素时,如果不加 1,mid会一直等于lo,而如果这时走的是lo = mid分支,区间就不会收缩,直接死循环。加 1 让mid向上取整,保证区间一定会缩小。这个道理我在纸上画了三次才彻底记住。

二分还有一个隐藏用法:答案二分。题目问"最小的时间""最大的容量"这类,往往是让你二分答案再校验可行性。这时校验函数写对是重点,二分框架就直接套上面的写法一。

3.3 前缀和与差分

前缀和的用途是把"区间求和"从 O(n) 降到 O(1)。写的时候务必多开一位,让pre[0] = 0,这样pre[r+1] - pre[l]就是闭区间[l, r]的和,不用特判l == 0。

def build_prefix(a): pre = [0] * (len(a) + 1) for i, v in enumerate(a): pre[i + 1] = pre[i] + v return pre # 闭区间 [l, r] 的和(0-indexed) def range_sum(pre, l, r): return pre[r + 1] - pre[l]

二维前缀和稍微绕一点,但套路一样:pre[i+1][j+1] = pre[i][j+1] + pre[i+1][j] - pre[i][j] + a[i][j],求子矩阵时用容斥,四块加减。这个公式我建议直接背下来,因为推导一遍要花不少时间。

差分是前缀和的逆操作,用来处理"给区间每个数加同一个值"这种批量修改。做法是在diff[l] += v、diff[r+1] -= v,最后对 diff 求一次前缀和就还原了。差分 + 前缀和这一对组合,几乎能秒掉所有"区间修改 + 单点查询"的题。

3.4 双指针与滑动窗口

双指针分两类:同向的双指针(快慢指针、滑动窗口)和相向的双指针(两数之和、有序数组)。滑动窗口的通用骨架我写成一个固定形状,遇到"最长/最短满足条件的子数组"就往里套:

def longest_window(a, ok): left = 0 best = 0 for right in range(len(a)): # 把 a[right] 加入窗口 while not ok(a, left, right): # 从左边收缩,直到窗口重新合法 left += 1 best = max(best, right - left + 1) return best

这里的关键在于ok函数的判断要能 O(1) 完成,通常靠一个计数器或哈希表来维护窗口内的状态。如果你在ok里又循环扫一遍窗口,那复杂度就退化成 O(n²) 了,这就白写了。

4. 数据结构模板:栈、堆、并查集与哈希

Python 内置的数据结构已经很强,但有几个地方的用法差异会导致性能天差地别,这一节专门讲这些"看起来一样其实不一样"的地方。

4.1 栈与队列:list 和 deque 的分工

栈就用list,append和pop都是 O(1),没有任何问题。

队列就不一样了。很多人图省事也用list,然后list.pop(0)出队——这一步是 O(n),因为它要把后面所有元素往前挪。队列操作 1e5 次,就是 1e10 级别的工作量,直接超时。正确做法是from collections import deque,用append和popleft,两个都是 O(1)。

from collections import deque q = deque() q.append(1) q.append(2) x = q.popleft() # O(1),不要用 list.pop(0)

顺便说一句,deque还能当双端队列用(appendleft、pop),也能通过maxlen参数做成固定长度的滑动窗口,用起来很舒服。

4.2 heapq 与堆的常见坑

heapq是最小堆,想要最大堆就把元素取负存进去。这里有三个我踩过的坑:

第一个坑是**heapq里的元素如果是元组,会比较到第二个元素**。如果你只想按第一个字段排序、第二个字段是对象,可能就会TypeError: '<' not supported。解决办法是在元组后面加一个自增的计数器做 tie-breaker:(priority, counter, item)。

第二个坑是**heapq不保证稳定**,同样的优先级出堆顺序不确定,别指望它按插入顺序。

第三个坑是**heapq没有decrease_key**。Dijkstra 里想更新某个点的距离,标准做法是直接再 push 一个新条目,出堆时判断这条是不是过期的(记一个dist数组比较一下),过期就跳过。这叫"懒惰删除",虽然堆里条目多了,但总复杂度还是 O(m log m),可以接受。

import heapq def dijkstra(n, adj, src): INF = float('inf') dist = [INF] * (n + 1) dist[src] = 0 pq = [(0, src)] while pq: d, u = heapq.heappop(pq) if d > dist[u]: continue # 过期条目,跳过 for v, w in adj[u]: nd = d + w if nd < dist[v]: dist[v] = nd heapq.heappush(pq, (nd, v)) return dist

4.3 并查集:路径压缩加按秩合并

并查集的模板必须背到能默写,因为"蚂蚁感冒""出差"这类题背后经常藏着连通性问题。标准写法是路径压缩 + 按大小合并,两个优化一起用,单次操作接近 O(1)。

class DSU: def __init__(self, n): self.p = list(range(n)) self.sz = [1] * n def find(self, x): p = self.p root = x while p[root] != root: # 先找根 root = p[root] while p[x] != root: # 再把路径上的点直接挂到根上 p[x], x = root, p[x] return root def union(self, a, b): ra, rb = self.find(a), self.find(b) if ra == rb: return False if self.sz[ra] < self.sz[rb]: ra, rb = rb, ra self.p[rb] = ra self.sz[ra] += self.sz[rb] return True

我把find写成迭代而不是递归,就是为了避开递归深度限制。递归版大概四行,看着清爽,但碰上退化链的时候会炸栈,赛场上不值得冒这个险。

4.4 哈希表与 Counter 的取舍

dict和collections.Counter是 Python 的杀手锏。计数、去重、分组,一行就能搞定。但要注意两点:

一是**Counter的most_common(k)内部是排序**,复杂度 O(n log n),如果你只是想要最大值,用max(cnt, key=cnt.get)更快。

二是哈希的键最好是不可变类型,元组可以,列表不行。做网格类搜索时,把坐标(x, y)直接当键存 visited,比给每个点编号再转成一维省事得多。

5. 搜索与图论模板:BFS、DFS 与最短路

搜索类题目在蓝桥杯里出现的频率非常高,尤其是省赛阶段,网格 BFS 几乎是"送分题"的定位——但前提是你得写得出来。

5.1 网格 BFS 的通用骨架

我把网格 BFS 抽象成一个固定骨架,只要改check判断和终点条件就能复用:

from collections import deque def bfs(grid, start, is_target): n, m = len(grid), len(grid[0]) dist = [[-1] * m for _ in range(n)] sx, sy = start dist[sx][sy] = 0 q = deque([start]) dirs = ((1, 0), (-1, 0), (0, 1), (0, -1)) while q: x, y = q.popleft() if is_target(x, y): return dist[x][y] for dx, dy in dirs: nx, ny = x + dx, y + dy if 0 <= nx < n and 0 <= ny < m and dist[nx][ny] == -1 \ and grid[nx][ny] != '#': dist[nx][ny] = dist[x][y] + 1 q.append((nx, ny)) return -1

几个细节值得说:dist数组同时承担"是否访问过"的职责,用 -1 表示未访问,省掉一个 visited 数组;方向的写法用元组循环,比手写四个 if 更不容易漏;边界判断写成0 <= nx < n,这是 Python 特有的链式比较,比nx >= 0 and nx < n顺眼。

如果题目要求"所有可能的路径数"或者"八方向连通块",只需要改dirs和dist的更新逻辑。如果是 BFS 求最短步数且边权相同,上面就是标准解;边权不同就得上 Dijkstra。

5.2 DFS 与回溯

DFS 我一般分两种用法。一种是只要遍历,不需要撤销状态,这种就用栈模拟或者直接递归。另一种是要枚举所有方案,那就得写回溯,路口选择、进入、撤销、退出四步齐全:

def backtrack(path, used, n, res): if len(path) == n: res.append(path[:]) # 一定要拷贝,否则存的是引用 return for i in range(n): if used[i]: continue used[i] = True path.append(i) backtrack(path, used, n, res) path.pop() # 撤销 used[i] = False # 撤销

这里最容易出的错就是res.append(path)而不是res.append(path[:])。因为path是同一个列表对象,你后面还会改它,最后 res 里全是空列表或者同一个终态。我第一次写全排列就栽在这,调了半小时才发现。

回溯的剪枝也很关键。排列组合类问题,如果某条分支已经不可能产生更优解,就提前 return。剪枝写得好,能把指数级搜索压到能过测试点的规模。

5.3 Dijkstra 与 Floyd 的选择

两个最短路算法什么时候用哪个,我总结了三条判断:

场景选谁理由
单源、边权非负、稀疏图Dijkstra + 堆O(m log m),最优
多源、节点数 ≤ 400FloydO(n³) 但写起来最短
边权有负Floyd 或 Bellman-FordDijkstra 会失效

蓝桥杯里 Floyd 的出镜率其实挺高,因为很多题的节点数只有一两百,三重循环 800 万次在 Python 里大概一两秒,能过。Floyd 的代码只有五行,性价比极高:

def floyd(n, dist): for k in range(n): dk = dist[k] for i in range(n): di = dist[i] dik = di[k] if dik == INF: continue for j in range(n): nd = dik + dk[j] if nd < di[j]: di[j] = nd

注意if dik == INF: continue这一句,它跳过了一大堆无效的加法,在稀疏图上能省掉很大一部分时间。

6. 动态规划模板:背包三件套与记忆化

DP 是区分省二和省一的分水岭,也是模板价值最高的地方。因为 DP 的状态定义和转移方程千变万化,但背包这一家子的代码骨架几乎不变。

6.1 01 背包

01 背包的关键在于内层循环从大到小遍历容量。为什么?因为如果从小到大,同一个物品可能在dp[j - w]里已经被放进去过一次了,就变成了"无限次",那就不是 01 背包而是完全背包了。这个"倒序"的细节是 01 背包唯一容易错的地方。

def knapsack_01(weights, values, cap): dp = [0] * (cap + 1) for w, v in zip(weights, values): for j in range(cap, w - 1, -1): # 倒序,保证每个物品只用一次 if dp[j - w] + v > dp[j]: dp[j] = dp[j - w] + v return dp[cap]

6.2 完全背包

完全背包把内层换成从小到大,因为物品可以用无限次:

def knapsack_full(weights, values, cap): dp = [0] * (cap + 1) for w, v in zip(weights, values): for j in range(w, cap + 1): # 正序 if dp[j - w] + v > dp[j]: dp[j] = dp[j - w] + v return dp[cap]

顺带说个判据:如果题目里物品数量没给,或者"可以取任意多个",那就是完全背包;如果明说"每个物品只能用一次",那就是 01 背包。看清这一点,直接决定循环方向。

6.3 记忆化搜索:想不出递推时的救命稻草

有些 DP 的状态转移不是按顺序来的,比如"从某个点出发能拿到的最大收益",这时用记忆化搜索比硬凑递推更省脑子。写法就是加个@lru_cache:

from functools import lru_cache @lru_cache(maxsize=None) def f(i, j): if i == 0: return 0 ...

用lru_cache有个前提:参数必须是可哈希的,不能传列表。如果状态里有数组,就得手动转成元组再传。另外递归深度也要提前放宽,不然状态一深就炸。

提醒:lru_cache的开销比手写数组大一些,状态数量在 1e6 以下还算能用,超过这个量级就老老实实写成递推数组。竞赛里我用它的原则是"能过就行,先拿分再优化"。

7. 数论与数学模板

数论题的特点是:知道模板就是几行,不知道就是一片空白。蓝桥杯填空题里经常出现"第 10000 个质数""依次排列后第 2024 位数字"这类,本质都是数论工具题。

7.1 质数筛与质因数分解

筛法我用埃氏筛的平方根优化版,写法短、够用:

def sieve(n): is_p = bytearray([1]) * (n + 1) is_p[0] = is_p[1] = 0 i = 2 while i * i <= n: if is_p[i]: is_p[i * i:: i] = bytearray(len(is_p[i * i:: i])) i += 1 return is_p

这里用了切片的批量赋值,把整段非质数一次性置 0,比逐个循环快很多。bytearray也比list[bool]省内存,1e7 的筛在内存上完全没压力。

单次质因数分解用试除法就够,只要试到i * i <= n:

def factorize(n): res = [] i = 2 while i * i <= n: while n % i == 0: res.append(i) n //= i i += 1 if n > 1: res.append(n) return res

7.2 GCD、快速幂与逆元

math.gcd是 C 实现,直接用,别手写欧几里得。快速幂是常用件,取模版本一定要记牢:

def fast_pow(base, exp, mod): res = 1 base %= mod while exp: if exp & 1: res = res * base % mod base = base * base % mod exp >>= 1 return res

顺便说一句,Python 的pow(a, b, m)三参数版本就是内置快速幂,直接调用比手写还快,因为它是 C 层的。但在需要理解原理或者做变形的题里,手写版本还是得会。

7.3 组合数与大整数

Python 的大整数是天然的,math.comb(n, k)直接算组合数,不用担心溢出。这在填空题里特别舒服——C++ 选手还在推模运算,你已经把答案打印出来了。

但要注意,如果题目要求取模后的结果,就不能用math.comb了,因为算出来的大数再取模,中间过程的规模可能爆炸。这时候要用杨辉三角或阶乘 + 逆元。杨辉三角适合 n 比较小(几百以内)的情况,写法就是两重循环C[i][j] = C[i-1][j-1] + C[i-1][j]。

8. 实战经验与问题排查

模板会背不等于能拿分,赛场上真正拉开差距的是调试速度和心态管理。这一节我把这些年踩过的坑整理成可以直接查的东西。

8.1 常见报错速查表

报错信息大概率原因处理方式
RecursionErrorDFS/BFS 递归太深开头加sys.setrecursionlimit,或改迭代
IndexError数组越界,常见于前缀和没多开一位检查pre长度是不是n+1
ValueError: too many values to unpack每行元素个数和预期不符改用data[idx]游标法
MemoryError二维数组开太大用一维化或bytearray
输出inf或-1最短路没连通,或初始化不对检查 INF 的初值和起点
KeyError用了defaultdict之外的方式访问不存在的键改用dict.get(k, 0)
结果是对的但超时循环里有 O(n) 操作被套进 O(n) 循环上前缀和、Counter、双指针

这张表我每次赛前都看一遍,因为报错本身不可怕,可怕的是你不知道它为什么报。

8.2 用对拍和小数据验证代替盲目提交

蓝桥杯的提交次数是有限的,所以每道题提交前,我都会做两件事:一是用手算的小数据对一遍,比如 n = 3 的情况,人肉推一遍答案,看代码输出是否一致;二是写一个暴力解法对拍。暴力解法通常就是三重循环枚举,几行代码,但能帮你验证复杂解法的正确性。

对拍的思路很朴素:随机生成 100 组小数据,跑一遍暴力、跑一遍优化版,结果不一致就说明优化版有问题。这个过程在本地花五分钟,能省掉赛场上一次提交机会。

8.3 时间分配:先拿满简单分

我自己的时间策略是这样的:开考后前十分钟把整张卷子扫一遍,标出"送分题""常规题""硬骨头"三类。然后先做送分题和常规题,填空题的答案能用暴力算的坚决用暴力,因为这些题只看答案不看代码效率。

剩下大概三分之一时间再啃硬骨头。这时候模板的价值就体现出来了——你不需要重新设计算法结构,只需要把模板粘出来,改状态定义和转移逻辑。省下的每一分钟思考时间,都是在给你最后一次机会做压轴题。

8.4 模板怎么整理才用得顺手

最后说个方法论。我整理模板不用任何花哨的工具,就是一个纯文本文件加清晰的注释头。每个模板前面写三行注释:这段代码解决什么问题、时间复杂度是多少、哪些地方需要改。第三行最重要,因为赛场上你需要的不是理解它,而是知道改哪里。

每隔一段时间,我会把官方题库里的历年真题重做一遍,凡是卡壳超过十分钟的题,就把当时缺失的模板补进去。这么滚了两年,模板文件大概稳定在三十来个片段,覆盖了绝大多数题型。剩下的题,基本就是这些片段的组合和变形了。

我个人在实际操作中的体会是:模板不是越全越好,而是越熟越好。你有三十个能默写的模板,远比有一百个需要翻查的模板管用。所以我建议你把上面这些代码亲手敲一遍,敲的过程中不要复制粘贴,让自己对每个边界条件形成肌肉记忆——考场上那几十分钟,靠的就是这个。

返回列表