PAT 乙级 1107 这道题,刷过的人应该都有印象:题面叫“老鼠爱大米”,看起来相当生活化,代码量也很短,但它不是那种轻松白送的送分题。我当年第一次做的时候就栽在最后一行的读入顺序上,本地样例全部通过,一提交就 WA,排查了半天才意识到问题根本不在算法,而在输入流的调用顺序不对。这也正是我从“看懂题目”到“真正会做”之间踩过的最值得记录的一步。这道题本质上是“分组求最大值 + 存在性判断”,非常适合刚学完循环和数组的初学者拿来练手,也适合正在备考 PAT 乙级的人在考前快速复盘读入输出这类基本功细节。如果你已经开始刷 PAT 乙级题库,1107 是你绝对绕不开的一道经典题。接下来我按照自己的理解,从破题、思路选型、代码实现、常见坑这几个角度完整拆一遍。1. 题目到底在问什么:先破题再动手1.1 一组大米一个最大值题面会给你两个正整数 N 和 M,N 代表小组数量,M 代表每个小组里的大米粒数。接下来的 N 行里,每行有 M 个整数,代表每一粒大米的重量。任务分两部分:第一,把每个小组里最重的那一粒找出来;第二,把这些“各组最大值”汇总起来,判断其中是否存在一个值等于最后给出的目标值 K。举个简单的例子,假设输入是:3 4 12 5 8 10 20 18 6 9 7 3 15 11 10这里 N=3,M=4。第一组最大值是 12,第二组是 20,第三组是 15,所以第一行输出就是12 20 15,然后判断这些最大值里是否存在一个数等于最后给的 10。12、20、15 都不等于 10,所以输出 NO。如果最后给的是 20,那么结论就是 YES。这个逻辑看起来简单到不需要多说,但真正在考试里丢分的点,恰恰藏在最后那个 K 的位置上。1.2 藏在最后一行里的“目标值”这道题最有迷惑性的地方,就是目标值 K 出现在所有成绩数据之后。很多人第一眼看到题,觉得无非就是“找最大值 + 判断存在性”,于是随手写出代码:先读 N 和 M,再循环读每一组,求完最大值就顺手和 K 比较。问题来了——K 此时还没有被读进来。在 C 语言里,如果变量 k 没有被赋值就直接参与比较,它的值是未知的,可能是上一次内存残留的随机值。本地跑的时候偶尔碰巧和预期一致,但一提交就大面积 WA。这个现象我在各个刷题群里见过不止一次,几乎每届考生都会有人掉进同一个坑里。另一个表层问题是“行”的概念。在 scanf 看来,空格、换行、Tab 都是统一的空白字符,它读取数据时并不关心你在哪一行。所以“最后一行给出 K”这个描述,在代码层面其实是“整数流中的最后一个整数”。如果你用循环去读 N 行、每行 M 个整数,那么循环结束后下一个 scanf 自然就会读到 K。这个顺序不能乱,乱了就是全盘崩塌。2. 核心思路选型:为什么要“存”而不是“边读边判”2.1 方案对比:二维数组、最大值数组、查表数组我第一次做这题时,第一反应是开一个二维数组把所有数据存下来,再统一处理。后来发现完全没必要。这里我把三种常见思路放在一起对比一下,大家可以根据自己的习惯选。方案核心做法优点缺点二维数组全存先用二维数组存下所有分数,再逐组求最大值思路直观,符合第一反应浪费空间,代码变长,没必要最大值数组边读边求每个小组的最大值,存入一维数组,最后统一判断空间开销小,逻辑清晰,推荐需要多开一个一维数组布尔标记数组把各组最大值作为索引打标记,读入 K 后直接查表判断变为 O(1),思想更进阶需要知道分数范围,可能有越界风险坦白说,这道题三种写法都能 AC,但“最大值数组”是我最推荐的一种。它既不浪费空间,又完全顺应题目的输入顺序:你无法在读到 K 之前判断,但只要先把各组最大值留存在数组里,读入 K 之后随时可以重新遍历判断。2.2 读入顺序才是真正的考点你已经看出来了,这道题真正的核心考点不是“怎么求最大值”,而是“怎么安排读入顺序”。很多人只盯着算法本身,忽略了输入流里的先后关系,结果被一个简单的顺序问题卡住。我用一句话概括正确流程:读入 N 和 M。循环 N 次,每次内层循环 M 次,读入每个成绩,实时比较得到本组最大值。把每组最大值存进数组。循环结束后,读入目标值 K。先按要求输出各组最大值,再遍历最大值数组判断是否存在等于 K 的值。每个环节都不能提前或延后。尤其是读 K 的这步,如果你在求最大值的过程中就试图拿 K 来比较,那么 K 还未被赋值;如果你在读取第一组数据前就去读 K,那么读到的其实是第一组的第一个成绩。这两种错误在本地测试时都不容易暴露,因为它们需要特定输入才会显现,这也是这种“简单题”容易让人翻车的原因。3. 完整 C 语言实现与逐步讲解3.1 可复现代码:最大值数组法直接上一份可以编译运行的 C 语言代码,这也是我最终提交的版本:#include <stdio.h> int main() { int n, m, k; scanf("%d %d", &n, &m); int maxs[105]; // 题目范围一般不大,105足够 for (int i = 0; i < n; i++) { int max = -1; // 初始化为比任何成绩都小的值 for (int j = 0; j < m; j++) { int score; scanf("%d", &score); if (score > max) { max = score; } } maxs[i] = max; } scanf("%d", &k); for (int i = 0; i < n; i++) { if (i > 0) { printf(" "); } printf("%d", maxs[i]); } printf("\n"); int flag = 0; for (int i = 0; i < n; i++) { if (maxs[i] == k) { flag = 1; break; } } printf("%s\n", flag ? "YES" : "NO"); return 0; }这段代码的核心逻辑很清晰,但有几个细节值得单独展开讲。3.2 关键细节一:max 的初始值为什么是 -1题目里的成绩是整数,而且通常是非负的。有人会把 max 初始化为 0,这在绝大多数情况下都没问题,因为如果读到的是正数,第一次比较必然更新 max;如果整个小组所有成绩都是 0,那么 max 保持 0,也是正确结果。但把 max 初始化为 -1 是一个更严谨的习惯。理由很简单:它不假设成绩的最小值一定是 0。万一题目把成绩范围调整为包含负数的情况,-1 依然能保证第一次读入的数据一定比 max 大,从而正确赋值。养成这个习惯之后,你会少踩很多“初始值选错导致答案整体偏差”的坑。3.3 关键细节二:输出格式的空格控制PAT 这类题目对输出格式要求非常严格,空格多一个、少一个都算格式错误(PE)。常见的输出要求是“各组最大值之间用空格分隔,行末不能有多余空格”。这行要求看着简单,但真写起来,很多人都会在最后一个数字后面多打一个空格。我的做法是:第一个数字前面不加空格,从第二个数字开始,每个数字前加一个空格。所以在代码里用if (i > 0) printf(" ")来判断。这样最后一个数字后面不会出现空格,完全符合要求。如果你看到别人的代码里有printf("%d ", maxs[i])这种写法,后面再单独退格,那是很不推荐的,不仅逻辑绕,而且容易在判题时触发格式错误。3.4 Python 版对照与思路验证有些同学习惯先用 Python 快速验证思路,再回 C 语言里实现。这里也给一个 Python 简版,方便对照理解:n, m = map(int, input().split()) maxs = [] for _ in range(n): nums = list(map(int, input().split())) maxs.append(max(nums)) k = int(input()) print(" ".join(map(str, maxs))) print("YES" if k in maxs else "NO")Python 的 input() 天然按行读取,所以“行”的语义会比 C 语言更直观一些,但核心逻辑完全一致:先把各组最大值存进列表,再读 K,最后统一判断。用这个版本跑通思路之后,再去写 C 版,就不容易在逻辑层面出错了。4. 常见错误与排查实录4.1 我踩过最深的三个坑下面这三个坑,我在刷题群和日常辅导里几乎每年都会看到,自己当年也亲身踩过,值得单独记录一下。第一个坑:读 K 的位置放错。有人觉得反正 K 最后才用,干脆在循环里顺手比较。错误代码类似于:for (int i = 0; i < n; i++) { int max = -1; for (int j = 0; j < m; j++) { scanf("%d", &score); if (score > max) max = score; } if (max == k) flag = 1; // 错:k 还没有被读入 }这里的 k 是未初始化的变量,在 C 语言里它的值是未定义的。本地跑的时候,可能碰巧和某个最大值不相等,flag 保持 0,你甚至察觉不到问题。但提交到在线评测系统后,结果就会飘忽不定,大概率是 WA。第二个坑:先去读 K。有人为了让“边读边判”成立,把scanf("%d", &k)放在了所有成绩读取之前。这样做的话,程序读到的所谓“k”,其实是第一组的第一个成绩。后续所有成绩都会错位,最终解析出来的 N 行数据全是乱的,输出自然不对。第三个坑:输出多了一个空格。这是最常见的格式错误。例如用printf("%d ", max)输出每个最大值,然后在最后单独处理换行,结果行尾多出一个空格,提交后直接 PE。这类错误不算算法问题,但同样会让你丢分,而且有时候比 WA 更难察觉,因为它只在你仔细比对输出时才会被发现。4.2 区分 WA 与 PE:从判题反馈定位问题在 PAT 这类在线评测系统里,提交后通常会有“答案正确”“答案错误”“格式错误”等不同反馈。很多新手一看到非 AC 的结果就慌,其实不同反馈已经给了你足够多的信息。如果是“格式错误(PE)”,优先检查空格、换行、大小写这些输出细节。不要怀疑算法,只要输出完全符合要求,这个反馈基本就会消失。如果是“答案错误(WA)”,那么大概率是逻辑或读入顺序问题。这时候不要盲目看代码,先拿几个手算样例跑一遍,尤其构造一个所有最大值都不等于 K 的用例,再看输出是否正确。如果是“段错误”或“运行时错误”,在这道题里基本不用考虑,除非你数组开得太小或者访问越界。我见过不少人看到 WA 就疯狂改算法,结果越改越乱。其实只要冷静分析一下,很多 WA 都出在读入顺序这种“看起来不太起眼”的地方。4.3 本地调试建议:打印中间结果新手阶段我非常推荐用“printf 调试法”。在这道题里,你可以在读入 K 之后、输出之前,打印所有中间变量:#ifdef DEBUG for (int i = 0; i < n; i++) { printf("maxs[%d] = %d\n", i, maxs[i]); } printf("k = %d\n", k); #endif本地编译时通过定义 DEBUG 宏开启这些打印,提交前注释掉即可。这样做的好处是,你可以直观地看到程序读到的 K 到底是什么、各组最大值是否计算正确。这比盯着代码发呆要高效得多。我个人的习惯是,凡是涉及“多个数据先后读入”的题,都会在本地打印一遍读入结果。因为读入一旦错位,后面的所有逻辑都是空中楼阁,你怎么调都调不对。5. 从 1107 延伸出去:这类题型的通用模板5.1 分组统计与存在性判断的模板做完 1107 之后,我意识到这类“分组求最值 + 存在性判断”的题,在 PAT 乙级里到处都是。它们的共同点是:输入先给若干组数据,每组若干个数。每组都要求出一个统计量,比如最大值、最小值、平均值。最后给出一个额外条件,要求判断所有组的统计结果中是否存在满足条件的值。目标条件往往放在最后,导致必须先读完所有数据再判断。于是我把这类题的代码框架总结成了一个模板,后面遇到类似的题直接套:// 模板框架 scanf("%d %d", &n, &m); for (int i = 0; i < n; i++) { int current = 初始值; for (int j = 0; j < m; j++) { scanf("%d", &x); // 根据题目要求更新 current } results[i] = current; // 保存当前组的统计值 } scanf("%d", &target); // 额外目标值,注意位置不能乱 // 输出与判断这个模板的复用价值很高。比如把求 max 换成求和、求平均,把“存在相等”换成“存在大于”,只需要改两三个地方,核心框架完全不用动。5.2 简单题里的细节陷阱PAT 乙级里其实没有太多算法难度特别高的题,很多人丢分全部丢在“看起来会做”的题上。1107 就是一个典型例子:它考察的不是高深的算法,而是你是否能在简单的流程里保持对输入输出细节的敏感。我在备考过程中养成了一个习惯:只要看到“先给一批数据,再给一个关键值”这种输入顺序,就会先把读入顺序在草稿纸上写一遍,再开始写代码。不要嫌这一步多余,因为很多“简单题”如果不在动手前想清楚读入顺序,写完之后再排查耗时反而更多。针对 PAT 乙级,我还会整理一份“易错点清单”,内容包括:scanf 的调用顺序是否和输入顺序一致、数组初始值是否覆盖所有合法输入、输出行末是否会有多余空格、循环边界是否存在差一错误。每次考试前看一遍这个清单,比临时刷好几道新题都管用。写在最后的个人体会如果要说我从 1107 这道题里收获最大的是什么,那一定不是“求最大值的方法”,而是“在写代码前先确认数据流顺序”的意识。这道题让我真正理解了:在线评测题目不会很复杂,但它会把你对基本功的掌握程度暴露得干干净净。尤其对于正在备考 PAT 乙级的朋友,我建议你不要急着刷难题,先把这些“简单但容易错”的题按类型整理好,反复练习读入、输出、边界处理这些基本功。等你把这些细节练到条件反射的地步,再去面对中等难度的题目,你会发现真正的思考空间要大得多。最后再分享一个小技巧:刷这类题时,本地多写几组边界用例。比如 N=1、M=1,比如所有成绩都是同一个值,再比如 K 恰好等于某一个最大值。把这些边界情况跑通,你的代码才算是真的稳了。
PAT 乙级 1107 这道题,刷过的人应该都有印象:题面叫“老鼠爱大米”,看起来相当生活化,代码量也很短,但它不是那种轻松白送的送分题。我当年第一次做的时候就栽在最后一行的读入顺序上,本地样例全部通过,一提交就 WA,排查了半天才意识到问题根本不在算法,而在输入流的调用顺序不对。这也正是我从“看懂题目”到“真正会做”之间踩过的最值得记录的一步。这道题本质上是“分组求最大值 + 存在性判断”,非常适合刚学完循环和数组的初学者拿来练手,也适合正在备考 PAT 乙级的人在考前快速复盘读入输出这类基本功细节。如果你已经开始刷 PAT 乙级题库,1107 是你绝对绕不开的一道经典题。接下来我按照自己的理解,从破题、思路选型、代码实现、常见坑这几个角度完整拆一遍。1. 题目到底在问什么:先破题再动手1.1 一组大米一个最大值题面会给你两个正整数 N 和 M,N 代表小组数量,M 代表每个小组里的大米粒数。接下来的 N 行里,每行有 M 个整数,代表每一粒大米的重量。任务分两部分:第一,把每个小组里最重的那一粒找出来;第二,把这些“各组最大值”汇总起来,判断其中是否存在一个值等于最后给出的目标值 K。举个简单的例子,假设输入是:3 4 12 5 8 10 20 18 6 9 7 3 15 11 10这里 N=3,M=4。第一组最大值是 12,第二组是 20,第三组是 15,所以第一行输出就是12 20 15,然后判断这些最大值里是否存在一个数等于最后给的 10。12、20、15 都不等于 10,所以输出 NO。如果最后给的是 20,那么结论就是 YES。这个逻辑看起来简单到不需要多说,但真正在考试里丢分的点,恰恰藏在最后那个 K 的位置上。1.2 藏在最后一行里的“目标值”这道题最有迷惑性的地方,就是目标值 K 出现在所有成绩数据之后。很多人第一眼看到题,觉得无非就是“找最大值 + 判断存在性”,于是随手写出代码:先读 N 和 M,再循环读每一组,求完最大值就顺手和 K 比较。问题来了——K 此时还没有被读进来。在 C 语言里,如果变量 k 没有被赋值就直接参与比较,它的值是未知的,可能是上一次内存残留的随机值。本地跑的时候偶尔碰巧和预期一致,但一提交就大面积 WA。这个现象我在各个刷题群里见过不止一次,几乎每届考生都会有人掉进同一个坑里。另一个表层问题是“行”的概念。在 scanf 看来,空格、换行、Tab 都是统一的空白字符,它读取数据时并不关心你在哪一行。所以“最后一行给出 K”这个描述,在代码层面其实是“整数流中的最后一个整数”。如果你用循环去读 N 行、每行 M 个整数,那么循环结束后下一个 scanf 自然就会读到 K。这个顺序不能乱,乱了就是全盘崩塌。2. 核心思路选型:为什么要“存”而不是“边读边判”2.1 方案对比:二维数组、最大值数组、查表数组我第一次做这题时,第一反应是开一个二维数组把所有数据存下来,再统一处理。后来发现完全没必要。这里我把三种常见思路放在一起对比一下,大家可以根据自己的习惯选。方案核心做法优点缺点二维数组全存先用二维数组存下所有分数,再逐组求最大值思路直观,符合第一反应浪费空间,代码变长,没必要最大值数组边读边求每个小组的最大值,存入一维数组,最后统一判断空间开销小,逻辑清晰,推荐需要多开一个一维数组布尔标记数组把各组最大值作为索引打标记,读入 K 后直接查表判断变为 O(1),思想更进阶需要知道分数范围,可能有越界风险坦白说,这道题三种写法都能 AC,但“最大值数组”是我最推荐的一种。它既不浪费空间,又完全顺应题目的输入顺序:你无法在读到 K 之前判断,但只要先把各组最大值留存在数组里,读入 K 之后随时可以重新遍历判断。2.2 读入顺序才是真正的考点你已经看出来了,这道题真正的核心考点不是“怎么求最大值”,而是“怎么安排读入顺序”。很多人只盯着算法本身,忽略了输入流里的先后关系,结果被一个简单的顺序问题卡住。我用一句话概括正确流程:读入 N 和 M。循环 N 次,每次内层循环 M 次,读入每个成绩,实时比较得到本组最大值。把每组最大值存进数组。循环结束后,读入目标值 K。先按要求输出各组最大值,再遍历最大值数组判断是否存在等于 K 的值。每个环节都不能提前或延后。尤其是读 K 的这步,如果你在求最大值的过程中就试图拿 K 来比较,那么 K 还未被赋值;如果你在读取第一组数据前就去读 K,那么读到的其实是第一组的第一个成绩。这两种错误在本地测试时都不容易暴露,因为它们需要特定输入才会显现,这也是这种“简单题”容易让人翻车的原因。3. 完整 C 语言实现与逐步讲解3.1 可复现代码:最大值数组法直接上一份可以编译运行的 C 语言代码,这也是我最终提交的版本:#include <stdio.h> int main() { int n, m, k; scanf("%d %d", &n, &m); int maxs[105]; // 题目范围一般不大,105足够 for (int i = 0; i < n; i++) { int max = -1; // 初始化为比任何成绩都小的值 for (int j = 0; j < m; j++) { int score; scanf("%d", &score); if (score > max) { max = score; } } maxs[i] = max; } scanf("%d", &k); for (int i = 0; i < n; i++) { if (i > 0) { printf(" "); } printf("%d", maxs[i]); } printf("\n"); int flag = 0; for (int i = 0; i < n; i++) { if (maxs[i] == k) { flag = 1; break; } } printf("%s\n", flag ? "YES" : "NO"); return 0; }这段代码的核心逻辑很清晰,但有几个细节值得单独展开讲。3.2 关键细节一:max 的初始值为什么是 -1题目里的成绩是整数,而且通常是非负的。有人会把 max 初始化为 0,这在绝大多数情况下都没问题,因为如果读到的是正数,第一次比较必然更新 max;如果整个小组所有成绩都是 0,那么 max 保持 0,也是正确结果。但把 max 初始化为 -1 是一个更严谨的习惯。理由很简单:它不假设成绩的最小值一定是 0。万一题目把成绩范围调整为包含负数的情况,-1 依然能保证第一次读入的数据一定比 max 大,从而正确赋值。养成这个习惯之后,你会少踩很多“初始值选错导致答案整体偏差”的坑。3.3 关键细节二:输出格式的空格控制PAT 这类题目对输出格式要求非常严格,空格多一个、少一个都算格式错误(PE)。常见的输出要求是“各组最大值之间用空格分隔,行末不能有多余空格”。这行要求看着简单,但真写起来,很多人都会在最后一个数字后面多打一个空格。我的做法是:第一个数字前面不加空格,从第二个数字开始,每个数字前加一个空格。所以在代码里用if (i > 0) printf(" ")来判断。这样最后一个数字后面不会出现空格,完全符合要求。如果你看到别人的代码里有printf("%d ", maxs[i])这种写法,后面再单独退格,那是很不推荐的,不仅逻辑绕,而且容易在判题时触发格式错误。3.4 Python 版对照与思路验证有些同学习惯先用 Python 快速验证思路,再回 C 语言里实现。这里也给一个 Python 简版,方便对照理解:n, m = map(int, input().split()) maxs = [] for _ in range(n): nums = list(map(int, input().split())) maxs.append(max(nums)) k = int(input()) print(" ".join(map(str, maxs))) print("YES" if k in maxs else "NO")Python 的 input() 天然按行读取,所以“行”的语义会比 C 语言更直观一些,但核心逻辑完全一致:先把各组最大值存进列表,再读 K,最后统一判断。用这个版本跑通思路之后,再去写 C 版,就不容易在逻辑层面出错了。4. 常见错误与排查实录4.1 我踩过最深的三个坑下面这三个坑,我在刷题群和日常辅导里几乎每年都会看到,自己当年也亲身踩过,值得单独记录一下。第一个坑:读 K 的位置放错。有人觉得反正 K 最后才用,干脆在循环里顺手比较。错误代码类似于:for (int i = 0; i < n; i++) { int max = -1; for (int j = 0; j < m; j++) { scanf("%d", &score); if (score > max) max = score; } if (max == k) flag = 1; // 错:k 还没有被读入 }这里的 k 是未初始化的变量,在 C 语言里它的值是未定义的。本地跑的时候,可能碰巧和某个最大值不相等,flag 保持 0,你甚至察觉不到问题。但提交到在线评测系统后,结果就会飘忽不定,大概率是 WA。第二个坑:先去读 K。有人为了让“边读边判”成立,把scanf("%d", &k)放在了所有成绩读取之前。这样做的话,程序读到的所谓“k”,其实是第一组的第一个成绩。后续所有成绩都会错位,最终解析出来的 N 行数据全是乱的,输出自然不对。第三个坑:输出多了一个空格。这是最常见的格式错误。例如用printf("%d ", max)输出每个最大值,然后在最后单独处理换行,结果行尾多出一个空格,提交后直接 PE。这类错误不算算法问题,但同样会让你丢分,而且有时候比 WA 更难察觉,因为它只在你仔细比对输出时才会被发现。4.2 区分 WA 与 PE:从判题反馈定位问题在 PAT 这类在线评测系统里,提交后通常会有“答案正确”“答案错误”“格式错误”等不同反馈。很多新手一看到非 AC 的结果就慌,其实不同反馈已经给了你足够多的信息。如果是“格式错误(PE)”,优先检查空格、换行、大小写这些输出细节。不要怀疑算法,只要输出完全符合要求,这个反馈基本就会消失。如果是“答案错误(WA)”,那么大概率是逻辑或读入顺序问题。这时候不要盲目看代码,先拿几个手算样例跑一遍,尤其构造一个所有最大值都不等于 K 的用例,再看输出是否正确。如果是“段错误”或“运行时错误”,在这道题里基本不用考虑,除非你数组开得太小或者访问越界。我见过不少人看到 WA 就疯狂改算法,结果越改越乱。其实只要冷静分析一下,很多 WA 都出在读入顺序这种“看起来不太起眼”的地方。4.3 本地调试建议:打印中间结果新手阶段我非常推荐用“printf 调试法”。在这道题里,你可以在读入 K 之后、输出之前,打印所有中间变量:#ifdef DEBUG for (int i = 0; i < n; i++) { printf("maxs[%d] = %d\n", i, maxs[i]); } printf("k = %d\n", k); #endif本地编译时通过定义 DEBUG 宏开启这些打印,提交前注释掉即可。这样做的好处是,你可以直观地看到程序读到的 K 到底是什么、各组最大值是否计算正确。这比盯着代码发呆要高效得多。我个人的习惯是,凡是涉及“多个数据先后读入”的题,都会在本地打印一遍读入结果。因为读入一旦错位,后面的所有逻辑都是空中楼阁,你怎么调都调不对。5. 从 1107 延伸出去:这类题型的通用模板5.1 分组统计与存在性判断的模板做完 1107 之后,我意识到这类“分组求最值 + 存在性判断”的题,在 PAT 乙级里到处都是。它们的共同点是:输入先给若干组数据,每组若干个数。每组都要求出一个统计量,比如最大值、最小值、平均值。最后给出一个额外条件,要求判断所有组的统计结果中是否存在满足条件的值。目标条件往往放在最后,导致必须先读完所有数据再判断。于是我把这类题的代码框架总结成了一个模板,后面遇到类似的题直接套:// 模板框架 scanf("%d %d", &n, &m); for (int i = 0; i < n; i++) { int current = 初始值; for (int j = 0; j < m; j++) { scanf("%d", &x); // 根据题目要求更新 current } results[i] = current; // 保存当前组的统计值 } scanf("%d", &target); // 额外目标值,注意位置不能乱 // 输出与判断这个模板的复用价值很高。比如把求 max 换成求和、求平均,把“存在相等”换成“存在大于”,只需要改两三个地方,核心框架完全不用动。5.2 简单题里的细节陷阱PAT 乙级里其实没有太多算法难度特别高的题,很多人丢分全部丢在“看起来会做”的题上。1107 就是一个典型例子:它考察的不是高深的算法,而是你是否能在简单的流程里保持对输入输出细节的敏感。我在备考过程中养成了一个习惯:只要看到“先给一批数据,再给一个关键值”这种输入顺序,就会先把读入顺序在草稿纸上写一遍,再开始写代码。不要嫌这一步多余,因为很多“简单题”如果不在动手前想清楚读入顺序,写完之后再排查耗时反而更多。针对 PAT 乙级,我还会整理一份“易错点清单”,内容包括:scanf 的调用顺序是否和输入顺序一致、数组初始值是否覆盖所有合法输入、输出行末是否会有多余空格、循环边界是否存在差一错误。每次考试前看一遍这个清单,比临时刷好几道新题都管用。写在最后的个人体会如果要说我从 1107 这道题里收获最大的是什么,那一定不是“求最大值的方法”,而是“在写代码前先确认数据流顺序”的意识。这道题让我真正理解了:在线评测题目不会很复杂,但它会把你对基本功的掌握程度暴露得干干净净。尤其对于正在备考 PAT 乙级的朋友,我建议你不要急着刷难题,先把这些“简单但容易错”的题按类型整理好,反复练习读入、输出、边界处理这些基本功。等你把这些细节练到条件反射的地步,再去面对中等难度的题目,你会发现真正的思考空间要大得多。最后再分享一个小技巧:刷这类题时,本地多写几组边界用例。比如 N=1、M=1,比如所有成绩都是同一个值,再比如 K 恰好等于某一个最大值。把这些边界情况跑通,你的代码才算是真的稳了。