不知道你有没有这样的经历:注册了一个在线评测账号,翻开“入门”题单,第一页就躺着一道看似特别短的小题,编号P4414,名字叫ABC。三个整数加一个长度为三的字符串,就能让不少人卡住十分钟。说它难,绝对谈不上;但你真要动手写,还真有几个地方值得琢磨。这道题很适合三类人:刚学完语法还没写过几道题的新手,想带新人入门的老手,以及想练一练“如何把英文题意翻译成代码思路”的普通刷题人。我带你把它拆开,从题意到实现,从坑点到背后的思维模型,一次看清楚。
1. 这题到底在考什么:拆穿入门题的“伪装”
1.1 把题目翻译成人话
P4414 ABC,题面本身很简短。大意是:给你三个整数,它们的值不一定按顺序给出,但题目保证这三个数之间存在着明确的“大小关系”。接着再给你一个由三个英文大写字母组成的字符串,只会出现A、B、C这三个字母,且每个字母恰好出现一次。这个字符串的作用是规定输出顺序。
举个例子,输入:
1 5 3 ABC那真实含义是:我们先假设这个三个值有一个从小到大的“集合关系”,按升序排列后,最小的那个数代表A,中间的数代表B,最大的数代表C。所以1、5、3经过排序后,A=1,B=3,C=5。字符串ABC表示按照A、B、C的顺序输出,结果就是“1 3 5”。
如果你输入的是:
1 5 3 BAC那就要先输出B对应的数3,再输出A对应的数1,最后输出C对应的数5,结果就是“3 1 5”。
题目本身没有任何复杂的算法,核心就是你得先给三个数排序,再根据字符串里的字母顺序取数。这个“排序”和“映射”的过程,才是真正的考点。
1.2 核心考点拆解
这题看着简单,实际上包含了三块基础能力,正好是竞赛入门的关键拼图。
第一块是排序。任何排序算法都能用,哪怕你手写冒泡排序都能过。数据量只有三个数,时间复杂度和空间复杂度都不是问题。这也说明一个道理:入门题往往不是考你算法多花哨,而是考你会不会在正确的地方用正确的操作。
第二块是“字母到数值”的映射。把A对应到排序后的最小数,B对应到中间数,C对应到最大数,这一步看起来不起眼,但特别能反映你对变量和数组索引的理解。很多新手就是栽在这里:明明已经排序成功,却不知道如何把字符串里的字母与数值关联起来。
第三块是输入输出格式控制。别小看题目里要求的“分数之间用空格分隔,行末没有多余空格”这类细节,很多人在正式比赛中因为多输出了一个空格而WA(Wrong Answer),这是最常见的低级失误之一。
所以这道题的“伪装”是简单模拟,实际上它考的是数据处理中最基本的三板斧:排序、关联、格式化输出。把这三样练扎实,后面很多题目都能顺下来。
2. 拿到题目后,先别急着写代码:思路设计
2.1 从需求推导到算法流程
我见过不少新手一看到题目有思路就立刻敲键盘,结果敲到一半发现逻辑漏洞百出。更稳妥的做法是先在纸上推一遍流程,哪怕只有三步也要写清楚。
这题的固定流程应该是:
- 读入三个整数,放进一个长度为3的数组或三个变量里。
- 对这三个数进行升序排序。
- 读入由三个字母组成的字符串。
- 遍历字符串的每个字符,根据字符是什么来决定取排序后数组中的哪个元素。
- 按题目要求的格式输出。
为什么先把排序做了?因为题目给的三个数本身并不保证有序,而我们最终要按A、B、C所代表的“大小层级”输出。A代表的是“第一小”,B是“中间”,C是“最大”。只有排完序,这三个字母才能分别对应到数组的索引0、1、2。排序这一步是整个方案的“地基”,没有它,后续映射全部乱套。
遍历字符串输出时,你会发现一个很自然的对应关系:
- 字符A -> 排序后数组的第0个元素
- 字符B -> 排序后数组的第1个元素
- 字符C -> 排序后数组的第2个元素
这本质上是一个字典映射,只不过这个字典只有三个键,完全可以用if-else或者switch表达,也可以用真正的字典/哈希表来写。用哪种方式不是关键,关键在于你脑子里要有“映射”这个概念。
2.2 为什么排序后可以直接用索引映射
很多人会问:直接把输入顺序当作A、B、C行不行?不行。因为输入的第一个数可能并不是最小值,甚至可能是最大值。例如输入:
5 1 3如果按输入顺序硬套,让第一个数等于A,那A就成了5,这明显违背题意。A必须始终代表“三数中的最小值”。
所以排序的意义是把“值的大小区间”固定下来。排序后,数组的下标天然就是等级,下标0对应最小等级,下标1对应中间等级,下标2对应最大等级。A、B、C这三个字母就只是等级的名字罢了。
这就是一个典型的“先抽象后实现”的思考过程。你不用关心数字具体是多少,只需要关心相对关系。很多入门题看似简单,但正是这种“相对关系”的建模能力,才决定了你能不能写出干净、不容易出错的代码。
3. 实操环节:用C++和Python各写一版(附逐行讲解)
3.1 C++实现:从输入到输出的完整代码
我习惯用C++来写这种排序题,因为sort函数真的省事。完整代码可以这样写:
#include <bits/stdc++.h> using namespace std; int main() { int a[3]; for (int i = 0; i < 3; ++i) { cin >> a[i]; } sort(a, a + 3); string s; cin >> s; for (int i = 0; i < 3; ++i) { char c = s[i]; if (c == 'A') { cout << a[0]; } else if (c == 'B') { cout << a[1]; } else { cout << a[2]; } if (i < 2) { cout << ' '; } } cout << endl; return 0; }这里有几个细节值得讲一下。
sort(a, a + 3)是对长度为3的数组整体排序,头文件我直接用了万能头#include <bits/stdc++.h>,在竞赛环境里非常方便,但如果你在工程类开发里写代码,还是建议按需包含头文件。
在输出映射部分,我用了一个if-else链。注意最后一个else不需要判断是不是'C',因为题目保证字符串只包含A、B、C三种字母,且每个字母出现一次。这种利用题目约束来简化代码的做法,在竞赛里很常见,但前提是你必须确认约束是成立的,否则就会藏下隐患。
输出空格的部分是一个很容易被忽视的细节。我选择在“当前字符后面不是最后一个字符”的时候输出空格,这样可以避免行末多一个空格。很多人喜欢写成cout << a[0] << ' ' << a[1] << ' ' << a[2] << endl;,虽然也能过,但如果题目后续要求动态输出长度,这种硬编码写法就不够通用了。
3.2 Python实现:更简洁的写法
如果你用的是Python,这道题的代码可以写得非常短,但简洁不代表可以不理解原理。我推荐这样写:
nums = list(map(int, input().split())) nums.sort() order = input().strip() mapping = { 'A': nums[0], 'B': nums[1], 'C': nums[2] } result = [str(mapping[ch]) for ch in order] print(' '.join(result))第一行把输入的一行数字转成整数列表,第二行调用sort()原地排序。这里有个小知识点:list(map(int, input().split()))是竞赛Python刷题的经典写法,一定要熟练掌握。它先split得到字符串列表,再用map把每个元素转成int,最后转成列表。
第四到第六行建立了一个字典,让字母映射到对应的排序后数值。这个字典就是解题思路里的“映射关系表”。相比C++的if-else,Python的字典表达更直观,也更接近人的思维。
最后两行用列表推导式把order里的每个字母转成对应数字的字符串,再用join拼成整体输出。注意join是字符串方法,参数必须是一个可迭代对象,并且里面的元素必须是字符串,所以我在列表推导式里做了str()转换。
3.3 代码测试与提交前自检
写完了不等于做完了。我强烈建议你在提交之前,至少做三组手动测试。
第一组是常规样例。比如输入:
1 5 3 ABC预期输出1 3 5,这能验证排序和顺序输出是否正常。
第二组是打乱顺序的字母串。比如输入:
20 10 1 CAB排序后A=1、B=10、C=20,CAB对应先输出20,再输出1,最后输出10,也就是20 1 10。
第三组是边界值。虽然题目通常会给正整数范围,但你仍然可以用负数或重复值测试自己代码的鲁棒性。比如输入:
5 5 5 ABC因为三个数相等,排序后无论怎么选数值都一样,输出应该是5 5 5。如果这种输入输出不对,说明你的映射逻辑存在顺序问题。
这三组测试覆盖了正常、乱序和边界三种情况,能筛掉九成以上的低级错误。
4. 我在刷这道题时踩过的坑和隐藏技巧
4.1 常见错误TOP榜
我见过太多人在这道题上栽跟头,自己当年也都踩过,下面这些坑最有代表性。
第一个坑是“不排序,直接映射”。有同学觉得输入顺序就是A、B、C的顺序,结果遇到逆序输入直接错。这是对题意理解不透彻导致的问题,不是代码能力问题。破解方法很简单:确认A、B、C与数值大小严格对应,而不是与输入位置对应。
第二个坑是“把字母和数值对应反了”。有人排序排对了,但映射写成A对应a[2]、C对应a[0],结果整个输出顺序颠倒。这种错误往往在于没有在纸上先写清楚A是最小值还是最大值。养成动手前先画图的习惯,这个坑就能避免。
第三个坑是“行末多空格”。虽然很多评测平台不会因为行末空格判WA,但有些严格的平台会。你永远不知道平台会用什么校验规则,所以最保险的做法就是程序里控制好空格,不要心存侥幸。
第四个坑是“读入字符串时带进了换行”。在C++里,cin >> s不会读入空格和换行,所以没问题。但在Python里,如果你不加strip(),input()返回的字符串可能带有末尾的换行符,直接拿去索引会出错。我在调试中经常遇到这种情况,所以Python读入非纯数字内容时务必处理空白字符。
第五个坑是“没理解题目保证输入数个数为三、字符串长度一定为三”。有些同学为了“通用”强行写循环遍历所有字母,结果字符串长度确实也是三,反而把问题复杂化。竞赛题中题目给的约束就是你可以利用的前提,在约束内做最简单的事,不要自行扩大需求。
4.2 这题的隐藏技巧:用映射表解耦逻辑
如果你把这道题写得足够清晰,会发现自己其实构建了一张“映射表”。无论你用C++的if-else,还是Python的字典,本质上都是在做查表动作。这个思路一旦建立,后面遇到很多题目都能受益。
比如洛谷的很多字符串处理题,会让你根据一个映射表把一个字符转换成另一个字符。这时候你不需要写几百个if-else,直接建一个数组或字典,一行就能查表。P4414 ABC就是这种“查表思想”最简单的训练场。
还有一个技巧是:把数值和字符串分开处理。先集中处理数值的排序,再集中处理字符串的映射,最后再考虑输出格式。分阶段编码能显著降低调试难度,也便于加注释。我推荐新手在代码里用注释把这三个阶段标出来,哪怕只是三行注释,也能让你的代码可读性提高不少。
4.3 从“三个数排序”到“任意数量排序”的思维迁移
你可能会想:P4414 ABC只是三个数,排序太简单了,有什么可学的?但别忘了sort不是只能排三个数。你可以把这道题稍微扩展一下:如果题目改成给你N个整数,再给一个长度为N的字符串,字符串里是a1,a2,...之类的代码,要求按代码顺序输出,你会怎么处理?
这时候你的思路会变成:先对N个数整体排序,然后建立某种顺序映射,再遍历代码串输出。虽然数据规模大了,但核心模型和P4414 ABC完全一致。这就是一种思维迁移能力的训练。所有复杂算法题,都是从这类简单题的思想上演化而来的。
5. 这题还能怎么玩:扩展与变体
5.1 变体一:如果三个数不保证有序怎么办
题目明确告诉你三个数之间可以排序得到A、B、C,但我们可以假设一个更恶劣的条件:输入三个数,再输入一个字符串,但字符串里的字母顺序并不是随便指定的,而是对应某个未知的初始排列。你需要自己判断A、B、C分别对应谁。
这种情况下,你仍然可以先排序,但此时A、B、C并不一定对应升序排列后的三个数。你需要先枚举所有可能的排列组合,一共只有6种,逐一检查是否满足题目中隐含的约束条件。这个思路相当于从一个确定解变成“可能解搜索”,进一步锻炼了暴力枚举的能力。P4414 ABC其实是“确定解”的情况,但理解暴力枚举后,你能更好地理解为什么题目要保证A<B<C。
5.2 变体二:如果字母变成了0、1、2怎么办
如果题目的字符串不是“ABC”,而是“132”之类的数字串,要求按数字串里的数字顺序输出排序后对应位置的元素,你会怎么做?
这其实把字符映射变成了数字索引映射。你可以把数字串里的每个字符先转成数字,然后直接当作数组下标去访问排序后的数组。这个变体,本质上就是处理“排序后按自定义顺序输出”的通用模板。如果你把P4414 ABC的代码抽象成“先排序,再按某个排列P输出对应元素”,那么你已经掌握了一个非常有用的函数式思维。
5.3 扩展实战:如何用这题的模板解“按身高排序后按名字顺序输出”的问题
我们再做一个贴近生活的迁移。假设你有三个人的身高,现在告诉你一个字符串BAC,表示“先输出第二高的人名,再输出最矮的人名,再输出最高的人名”。你已经拿到这三个人的姓名列表,要求按这个顺序输出对应的人名。
这个扩展题的难点在于姓名和身高不能单独排序,它们是一一对应的。如果你只对身高排序,姓名顺序就乱了。解决方法可以先建立一个结构体/元组,或者用索引数组来维护对应关系。P4414 ABC之所以简单,是因为数值本身没有附属信息,而现实问题往往有附属信息。这个区别是入门选手比较容易忽略的进阶点:当你需要处理多字段数据时,排序不能破坏字段间的对应关系。
不过P4414 ABC本身用不到这些,但作为入门题,它给了你一个很好的起点。你可以在AC后再做一次“自己给自己出题”的练习,好好体会一下从入门模板到实际应用的思维方式。
6. 写在最后的一点个人体会
这道题我在很多场合推荐过,带新人时尤其喜欢拿它当第一道模拟题。原因很简单:它不涉及任何高深的算法,但又包含了“读入、排序、映射、格式化输出”这四个刷题高频步骤。一个人如果能在不看题解的情况下独立完成这道题,说明他已经具备了解决八成简单模拟题的基础。
我印象很深的是,有次一个新人跑来问我,为什么自己写的代码在测试样例上没问题,提交后却WA。后来一查,他把输入判断成“三个数已经按从小到大排好了”。这个教训不是代码层面的,而是读题层面的。从那以后,每次带新人刷题,我都会强调一个习惯:题目没说完的信息,永远不要自己脑补。P4414 ABC,就是在教你这件事。
如果你能把这题的思路彻底吃透,再去刷后面的排序、模拟题,会发现很多题都有相似的影子。刷题从来不是刷数量,而是刷模型。一道简单的ABC,练好了,能辐射出一大片。