☰
PAT乙级1002题解:写出这个数,避开新手常见坑
2026/10/1 13:52:53 网站建设 项目流程

PAT乙级1002这道题,在OJ圈子里几乎算得上“梦开始的地方”。我记得自己刚接触PAT那会儿,第一次正经跑通的题就是它——题目短,需求直白,但就是这种看似简单的题,反而最能暴露基本功的窟窿。想写这篇东西其实有个很直接的起因:我前前后后帮不少刚入坑算法题的朋友看过代码,发现很多人在1002上不是不会做,而是栽在一些特别低级又特别隐蔽的细节上。所以这篇就围绕PAT乙级1002展开,既把标准解法讲透,也把容易丢分的地方掰开揉碎说清楚。不管你是刚开始刷PAT的新手,还是想帮别人讲题的老手,这内容应该都能让你有点收获。

1. 题目本身在说什么:一次拆完,别留疑问

1.1 原题要求只有三句话

PAT乙级1002,题目名是“写出这个数”。原题描述不复杂:读入一个正整数 n,计算它的各位数字之和,然后用汉语拼音写出这个和的每一位数字。

这里有几个关键信息要圈出来:

  • 输入是一个正整数,不是负整数,也不是小数。
  • 要求的不是输出这个数本身,而是先做一个“数位求和”,再对和做“拼音转换”。
  • 输入上限是 n < 10^100,也就是最多100位十进制数。
  • 输出的每个拼音之间要有一个空格,行末不能有多余空格。

举个例子:输入1234567890987654321123456789,先算数位和。这串数拆开加一遍,结果是135,那么输出就是yi san wu。注意是“三”的拼音san,不是“山”;是“五”的拼音wu,不是“无”。这种细节就是PAT判题系统最喜欢埋的点。

1.2 它真正在考察的四个基本功

别看题目简单,它其实同时考察了四个东西。

第一,输入输出格式的严谨性。PAT的评判对格式要求非常苛刻,最后多一个空格、少一个换行、拼音大小写写错,都可能直接判错。很多人算法思路没问题,却在格式上反复交学费。

第二,大数处理意识。看到“正整数 n”,第一反应是int,这确实是新手默认思路。但题目明确写了n < 10^100,这个量级根本不是C语言里任何整型能装下的,long long最大也就约9.2×10^18,差远了。所以必须换赛道,用字符串来接收输入。

第三,字符与数字的转换。用字符串读入之后,每一个字符都是'0'到'9'这样的ASCII字符,要参与加法运算就得先转换成对应的整数值。最常见也最标准的方式就是s[i] - '0'。

第四,数组映射和输出拼接。把0到9的拼音放进一个字符串数组,然后用和值的每一位数字去索引对应的拼音,这是一种非常基础的“查表”思路。后面做进制转换、做哈希映射的时候,这个思路会反复出现。

1.3 为什么说是“开胃菜”,却总有新手翻车

我在不同的学习群里看过不下二十个版本的1002代码。大部分人第一次都能写出来一个“看起来能跑”的版本,但真正提交上去就发现过不了。

翻车集中在几种情况:

  • 用int n; scanf("%d", &n);接收输入,一提交才发现运行错误或者答案错误。
  • 把每一位数字直接相加时忘记减去'0',得到的和莫名其妙地比预期大很多。
  • 拼音表只写了9个元素,访问pinyin[9]的时候越界或者输出为空。
  • 最后多打了一个空格,被系统提示“格式错误”。

说实话,这些错误单独拎出来都不值一提,但它们恰恰说明一个问题:刷题的第一个门槛不是算法,是“准确理解题目到底要什么,并且严格按照要求输出”。搞定这一点,1002就是一道热身题;搞不定,后面每道题都会在同一个坑里摔一次。

2. 解题思路演进:从直觉方案到正确方案

2.1 第一反应为什么必然翻车

看到“算各位数字之和”,绝大多数人的第一反应是:

int n; scanf("%d", &n); int sum = 0; while (n > 0) { sum += n % 10; n /= 10; }

这个思路本身没错,错在第一步。题目输入上限是10^100,也就是最多100位数字。C语言里int通常只有32位,最大才21亿多;long long虽然有64位,最大也就约922京(9.22×10^18)。100位十进制数远远超出这个范围。

可以直观感受一下:10^100这个数是1后面跟100个0,哪怕你用long long,也只能准确表示到10^18左右,高位数据全部丢失。一旦数据溢出,计算出来的数位和就错得离谱。

所以这道题的第一课就是:看到超大数,别往数值类型里装,老老实实用字符串。这不是什么高深的技巧,就是一个工程常识——数据的存储方式要跟数据本身的形态匹配。

2.2 字符串读入才是正解

正确的做法是把输入当成一串字符处理。C语言里用scanf("%s", str)读入,或者用C++的cin >> str,Python直接input(),Java用Scanner.next()。它们的共同点是:不关心输入数字有多大,只按字符逐个读入。

读进来之后,数位和的计算就变成了遍历字符串:

int sum = 0; int len = strlen(str); for (int i = 0; i < len; i++) { sum += str[i] - '0'; }

这里有两个容易犯的毛病。第一个是把str[i] - '0'写成str[i]。如果输入的第一个字符是'1',它的ASCII码是49,你直接加进去,求和结果就会偏大48。第二个毛病是用strlen的时候忘了带头文件<string.h>,编译直接报隐式声明警告,虽然有些环境下还能跑,但属于定时炸弹。

至于数位和的存储类型,int完全够用。因为输入最多100位,每一位最大是9,和的最大值不会超过900,一个int装得绰绰有余。这也是题目设计里比较体贴的地方:输入很大,但中间结果很小。

2.3 和的每一位怎么拆,各有各的写法

数位和算出来之后,面临第二个问题:把和的每一位按从左到右的顺序输出拼音。

和的最大值是900,最多三位,所以方法很多。第一种是直接用sprintf把整数转成字符串:

char res[100]; sprintf(res, "%d", sum);

然后遍历res,把每一位字符转换成数字,再去查拼音表。这个方案最直接,而且因为sprintf天然把整数转成高位在左的字符串,输出顺序不用额外处理。

第二种是“除10倒着存”。循环里用sum % 10依次取出个位、十位、百位,存进数组,最后逆序遍历数组输出。这个方案不依赖sprintf,但要多写几行,而且容易在逆序的时候把下标搞错。

第三种是递归输出,代码很优雅:

void print_sum(int sum) { if (sum / 10) { print_sum(sum / 10); } printf("%s", pinyin[sum % 10]); }

递归进去再出来,天然保证了高位先输出。不过新手对递归还不熟的时候,不建议为了炫技用这种方式,等理解透彻了再用不迟。

我个人更推荐sprintf方案。理由很简单:可读性好,代码短,不容易在边界条件上犯错。刷题的首要目标是正确AC,不是写得最玄乎。

3. C语言标准实现,与那些必须知道的输出细节

3.1 一份可以直接AC的参考代码

下面这份是我很常用的C语言写法,干净、直白,适合作为模板:

#include <stdio.h> #include <string.h> int main() { char str[105]; scanf("%s", str); int sum = 0; int len = strlen(str); for (int i = 0; i < len; i++) { sum += str[i] - '0'; } char *pinyin[] = {"ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu"}; char res[100]; sprintf(res, "%d", sum); int res_len = strlen(res); for (int i = 0; i < res_len; i++) { if (i != 0) { printf(" "); } printf("%s", pinyin[res[i] - '0']); } printf("\n"); return 0; }

这里我开了char str[105]而不是char str[101],纯粹是习惯性多留一点余量。题目说输入不超过100位,加上字符串结尾的'\0',最低要求是101个字符,开105不会错。

char *pinyin[]这种写法表示一个字符串数组,每个元素指向一个字符串常量。索引从0到9,刚好对应数字0到9的拼音。注意不要写成char pinyin[],那是二维字符数组的另一种声明,容易跟一维数组混淆。

3.2 输出格式的魔鬼细节

PAT对输出格式的检查非常细,我见过太多人死在空格上。看输出部分:

for (int i = 0; i < res_len; i++) { if (i != 0) { printf(" "); } printf("%s", pinyin[res[i] - '0']); }

这个写法的逻辑是:第一个拼音前面不输出空格,从第二个开始,每个拼音前面补一个空格。这样出来的结果就是yi san wu,而不是yi san wu(末尾多空格),也不是yisanwu。

反面写法是:

for (int i = 0; i < res_len; i++) { printf("%s ", pinyin[res[i] - '0']); }

这个会在最后一个拼音后面也带一个空格。如果PAT判题系统对行末空格做精确比对,这就是一个“格式错误”或“答案错误”。

另一个细节是换行。大部分PAT题目要求输出最后带一个换行符,所以我在最后加了printf("\n")。有的同学觉得不带换行也能过,确实有些题目的判题逻辑不卡这个,但不是所有题都这样。与其赌运气,不如每条输出都规规矩矩带换行。

3.3 关于“和值为0”这种边界情况

PAT的输入明确是正整数,所以理论上数位和最小也是1,不会出现0这种情况。比如输入1000,数位和是1,输出yi。但如果题目改成了“自然数”,允许输入0,那数位和就是0,应该输出ling。

在我上面这份参考代码里,sprintf(res, "%d", 0)得到的是字符串"0",循环遍历时res[0] - '0'恰好是0,查表得ling。也就是说,这段代码天然兼容了sum == 0的情况,不用额外加if判断。这个设计纯属顺手,但确实省心。

当然,如果要追求绝对的严谨,可以在计算完后加一句:

if (sum == 0) { printf("ling\n"); return 0; }

两种写法都行,看个人习惯。我平时倾向于让代码自然包含边界,避免为不存在的输入增加分支。

3.4 实测感受与编译注意事项

我用这份代码在PAT系统提交过,编译选项是标准自带,直接AC,运行时间几乎为0毫秒,内存占用几百KB。就这道题的规模来说,性能和内存根本不构成任何压力,别担心。

需要注意几个编译级别的问题:

  • 一定要#include <string.h>,否则strlen没有声明。
  • sprintf需要<stdio.h>,这个一般不会漏。
  • 如果用的是较新编译器,scanf可能建议改成scanf_s,但PAT的OJ环境是Linux下的GCC,直接scanf没问题。

还有,不要在代码里用gets(str)来读输入。gets不检查缓冲区边界,早就被移出标准了。而且读入的内容可能带换行,处理起来反而麻烦。scanf("%s", str)会跳过空白字符,输入又是单个数字串,用它最干净。

4. 换个语言再看一遍,Python、Java和C++的写法差异

4.1 Python:三行代码,但有一个反直觉的坑

如果允许用Python刷PAT乙级,1002的解法可以写得非常短:

s = input() total = str(sum(int(c) for c in s)) pinyin = ["ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu"] print(" ".join(pinyin[int(ch)] for ch in total))

核心逻辑就三行。int(c) for c in s把输入的每个字符转成整数,sum()求和,再str()转回字符串,最后逐位查表并用空格拼接。

但这里面有个反直觉的地方:如果直接用input()读,假设输入是"0"(虽然PAT不给这种数据),sum(int(c) for c in s)是0,str(0)是"0",输出ling,没问题。真正要注意的是Python的int不限制大小,你其实可以不转字符串直接求,但那样做没有任何好处,反而让代码绕了一道。

另外,Python里input()一次读一行,如果OJ的测试数据文件末尾有额外换行,input()会自动去掉,不用额外strip()。但如果你用了sys.stdin.read()读整个文件,就得小心末尾换行被当成输入的一部分。

4.2 Java:用String接收,再用charArray遍历

我第一次用Java写这题时,第一反应是BigInteger,后来想想纯属多此一举。因为既然只是数位求和,根本不需要把输入变成数值,直接按字符串处理就行。

import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String n = sc.next(); int sum = 0; for (int i = 0; i < n.length(); i++) { sum += n.charAt(i) - '0'; } String[] pinyin = {"ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu"}; String total = String.valueOf(sum); StringBuilder sb = new StringBuilder(); for (int i = 0; i < total.length(); i++) { if (i > 0) { sb.append(" "); } sb.append(pinyin[total.charAt(i) - '0']); } System.out.println(sb.toString()); } }

这里的坑在于String.charAt(i)返回的是char,直接参与加法时要记得- '0'。另外用StringBuilder拼接字符串比+连接更高效,虽然在1002这种小规模问题上差距可以忽略,但养成好习惯后面处理长字符串时会受益。

Java类名必须是Main,不然OJ找不到入口。这个忘了我第一次也犯了,很冤枉。

4.3 C++:string和数组的结合最顺手

C++的写法其实和C很像,但std::string让长度获取和字符遍历更自然:

#include <iostream> #include <string> using namespace std; int main() { string s; cin >> s; int sum = 0; for (char c : s) { sum += c - '0'; } string pinyin[] = {"ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu"}; string total = to_string(sum); for (int i = 0; i < total.size(); i++) { if (i != 0) { cout << " "; } cout << pinyin[total[i] - '0']; } cout << endl; return 0; }

C++11之后的范围for让字符遍历变得很舒服。这里唯一要注意的是total[i]返回的是char,和Java一样要减去'0'再当下标用。

我自己的体会是:这道题用C语言写最能理解底层细节,用Python写最省事,用C++写最均衡。如果你还在学习阶段,建议至少把C语言的版本完整手敲一遍,别一上来就用Python带过,否则会错过字符串和字符转换这个关键基本功。

5. 从1002说开去:PAT乙级刷题路线与常见教训

5.1 通过这道题之后,我建议的刷题顺序

1002在PAT乙级里的位置相当于“体检项目”,它帮你在入门阶段确认自己有没有方向性的大问题。通过之后,可以按照下面这个顺序继续:

题号题目要点为什么推荐放在前面
1001(3n+1)猜想循环和条件判断的入门组合
1008数组元素循环右移理解数组下标运算和取模
1011A+B 和 C基础输入输出,注意数据范围
1016部分A+B字符处理和数位拼接
1037在霍格沃茨找零钱进制换算思想,趣味性强

不要一上来就挑战高分难题。乙级题目的特点是“考基本功不考偏题”,把上面这些题刷完,你基本上就把循环、数组、字符串、格式化输出这些核心能力盘活了。之后再往上走,去看甲级的时候才会轻松一些。

5.2 PAT乙级的“十年老坑”清单

刷题数量上来之后,很多错误其实是重复的。我总结了一张高频踩坑清单:

错误类型典型表现正确做法
数据范围判断错误用int读大数,溢出后答案错见“超大数”就考虑字符串
字符忘记转数字sum += s[i],结果偏大用s[i] - '0'
拼音表越界pinyin[sum],sum超过9先转成字符串逐位处理
输出末尾带空格每个拼音后都加空格第一个前面不加,之后补空格
域名拼写错误“si”写成“shi”,“ba”写成“pa”多读题示例,提交前自查
类名/功能名问题Java类名不是Main按OJ要求命名

这些坑没有一个是“算法不会”,全是“审题不细”或“基础不稳”。但它们造成的扣分比想象中严重,因为PAT判题是零容忍的,一个字符不符就是整个测试点不得分。

5.3 比代码更重要的能力:读题和自测

我知道很多人刷题的习惯是看到题目就上手写代码,恨不得三分钟提交。但1002这种题最能教育我们:读题两分钟,胜过改码两小时。

拿到题之后,先做三件事:

  1. 圈出输入范围。看到10^100,第一时间想到字符串。
  2. 圈出输出格式。看到“拼音之间空格分隔”,第一时间想到循环拼接规则。
  3. 手算一遍示例。把输入的示例自己算一遍,确认和输出对得上,再开始写代码。

这三件事看着不起眼,却能避开至少80%的基础错误。我后来养成的习惯是:写完代码之后,先把题目给出的示例跑一遍,再自己构造两组边界数据——一组是最大长度输入,一组是可能导致和值最大的输入(比如100个9)。这两组数据如果都能正确输出,这道题基本就稳了。

6. 个人操作中的一点体会

最后聊点我自己的经验吧。实话实说,1002不是一道能让人产生“我很强”错觉的题,它更像是算法路上的一把尺子:量一量你对输入输出、数据类型、字符处理这些基本功掌握到什么程度。

我见过有的同学在这道题上反复提交七八次,每次都被不同的小细节挡住,然后很沮丧地来问我是不是不适合写代码。其实完全不是。PAT这种判题系统就是很“死板”,它不会因为你思路对就手下留情,任何格式问题都是错误。但这恰恰是好事——早早在入门题上被教做人,总比到了职场交付项目时被线上Bug折磨要强。

如果你现在正在1002上挣扎,我的建议是:不要急着看别人的满分代码,先把你自己写的版本跑一遍,一个测试点一个测试点地看错误输出,找到问题所在。改完再提交,再发现问题再改。这个过程本身,比最终那个AC的绿色结果值钱得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询