
PTA上币值转换这道题大概是很多人在C语言阶段遇到的第一道“非纯计算”题。前面几题还在处理水仙花数、逆序输出到这题突然要你按人民币大写方式输出中文很多人的第一反应是“这也太像语文题了”。题目本身不绕输入一个不超过9位的非负整数把它转成大写中文金额数字用壹贰叁肆伍陆柒捌玖单位用拾佰仟万亿最难收拾的就是零字。我见过最多的提交错误不是语法问题而是1001被输出成“壹仟零零壹”100001000被输出成“壹亿壹仟元整”少了一个“零”整道题就废了。这篇文章我会把规则一条条拆开给出一份可以直接提交的C语言代码再用测试用例把边界卡住最后附一个Python版本。准备刷PTA币值转换的人照着这个思路写基本不会跑偏。1. 题目到底在问什么先把人民币大写规则对齐1.1 最常见的题目版本PTA上这道“币值转换”通常是这样描述的输入一个整数n0 ≤ n ≤ 999999999输出它对应的人民币大写形式。比如输入123456789输出“壹亿贰仟叁佰肆拾伍万陆仟柒佰捌拾玖元整”。有些题目集末尾可能不要求带“元整”只输出大写数字这个差异只是最后多一句打印的事。为了讲清楚整体逻辑下面样例统一按带“元整”来写你去交题时如果发现题目样例没有“元整”把代码最后那行的“元整”去掉即可。输入输出0零元整10壹拾元整1001壹仟零壹元整100001000壹亿零壹仟元整123456789壹亿贰仟叁佰肆拾伍万陆仟柒佰捌拾玖元整是不是觉得挺有规律规律就是中文数字每四位一节个、十、百、千是一节万和亿是节名。123456789从右往左拆就是123456789也就是1亿 2345万 6789。程序里也一样按亿、万、个三个四位组分头处理最后把结果拼起来。1.2 先把数字和单位映射成常量代码里首先要准备两张表一张存0到9的大写数字一张存十百千的单位。const char *num[] {零, 壹, 贰, 叁, 肆, 伍, 陆, 柒, 捌, 玖}; const char *wei[] {, 拾, 佰, 仟};wei的下标0对应“个位”不输出单位下标1对应“拾”下标2对应“佰”下标3对应“仟”。比如某一位上的数字是5又处在百位就是“伍佰”。把题目拆成“四位一组”之后每组的内部转换逻辑完全一样输入一个0到9999的数输出这一小段的读法。至于这一组后面要不要挂“万”、要挂“亿”那是外面主流程的事。这种分而治之的思路比直接对着整个数字逐位判断要清爽得多。2. 最容易翻车的四种零场景这题的隐藏考点几乎全在零上面。非零数字和单位顶多就是拼接顺序但零的读法涉及中文习惯经常让人绕进去。我总结出四种必考场景。2.1 连续零只读一个零1001念“壹仟零壹”不是“壹仟零零壹”。中间有两个0中文只保留一个“零”。所以代码里碰到0时不能立即printf(零)而是设置一个“待输出零”的标记等后面遇到第一个非零数字时只补一次“零”然后立刻把标记清掉。这样不管中间有多少个0最终都只读一个零。2.2 节内前导零在节间要补零100010001这个数拆成亿1、万1、个1表面上看三节都不是零很容易直接拼成“壹亿壹万壹”。但正确读法是“壹亿零壹万零壹”。为什么因为万节的值是1不够1000也就是说万节内部最高位千万位是0亿和万之间存在一个“零窟窿”所以必须在“壹万”前面补一个零。个节也一样个节的值1不够1000前面有较高输出就还要补零。代码里的wan 1000、ge 1000就是在识别这种“节内前导零”。2.3 整节为零时不能乱读最隐蔽的是这种100001000拆成亿1、万0、个1000。中间万节整个都是0你不能输出“零万”因为中国人不会说“一亿零万一千”。但“个节”之前又必须有一个“零”所以正确结果是“壹亿零壹仟元整”而不是“壹亿壹仟元整”。这就要靠一个跨节的“空洞标记”来记住了。主流程里如果万节整节为零但前面已经输出过亿我就把gap标记置1等到输出个节时看到这个标记先补一个“零”。这里有个前提如果万节为零但是前面没有输出过亿比如1000这种gap不能被误置否则会输出“零壹仟”。所以代码里用的是else if (has)必须确认前面有输出。2.4 个位为零不读零但整个数为零一定要读零1000是“壹仟元整”末尾的两个0不读但0是“零元整”。这两件事看起来矛盾其实规则是一致的0只有在后面还有非零数字时才需要读末尾的0永远不读。整个输入是0是唯一特殊的边界单独判断一次就好。3. C语言实现四位一组转换把问题拆成两层3.1 先写节内转换函数printSection这个函数负责把0到9999范围内的任意一个数转换成中文读法。比如给它101它要输出“壹佰零壹”给它10输出“壹拾”给它1000输出“壹仟”。#include stdio.h const char *num[] {零, 壹, 贰, 叁, 肆, 伍, 陆, 柒, 捌, 玖}; const char *wei[] {, 拾, 佰, 仟}; void printSection(int x) { int d[4] {x / 1000, x / 100 % 10, x / 10 % 10, x % 10}; int start 0; while (start 4 d[start] 0) { start; } if (start 4) { return; } int zero 0; for (int i start; i 4; i) { if (d[i] 0) { zero 1; } else { if (zero) { printf(零); zero 0; } printf(%s%s, num[d[i]], wei[3 - i]); } } }核心是start和zero两个变量。start把节内前导的0都跳过比如输入100d数组是{0,1,0,0}start会停在1直接输出“壹佰”。zero就是前面说的待输出零标记它不会在遇到0时立刻输出而是挂在那边遇到下一个非零数字时才补一个“零”所以能自动解决连续零的问题。3.2 主流程亿、万、个三级拼装主函数里先按亿、万、个拆成三个四位组然后从上到下逐组输出。每一组输出之前都要判断前面有没有输出过内容这一组前面是不是存在需要补零的空洞int main() { int n; scanf(%d, n); if (n 0) { printf(零元整\n); return 0; } int yi n / 100000000; int wan (n % 100000000) / 10000; int ge n % 10000; int has 0; int gap 0; if (yi 0) { printSection(yi); printf(亿); has 1; } if (wan 0) { if (has (gap || wan 1000)) { printf(零); } gap 0; printSection(wan); printf(万); has 1; } else if (has) { gap 1; } if (ge 0) { if (has (gap || ge 1000)) { printf(零); } printSection(ge); } printf(元整\n); return 0; }has表示“当前已经输出过更高位的数字”gap表示“高一位的整节为零形成了空洞”。这两个变量加在一起就能处理第2节里所有零场景。3.3 为什么不是逐位硬编码有人可能会问直接拿一个数字字符串从高位往下逐位判断不是更直观吗对很短的数确实可以但位数一长判断条件会变成一团乱麻。比如万位要不要输出“万”千万位输出“仟”还是“仟万”百万位是“佰”还是“佰万”这些规则交织在一起很容易出现某一个组合漏判。四位一组的好处是把“一个四位数怎么读”和“四位数之间怎么衔接”彻底分开每一层都能单独测试。如果你拿到的题目末尾不要求“元整”就把最后的printf(元整\n)改成printf(\n)。也可以加一个宏开关方便切换版本#define NEED_YUAN_ZHENG 1再到最后输出处判断一下交题时改宏就行。4. 边界测试用例和调试经验代码写完只是第一步PTA这套题能不能过关键看边界测试。我整理了一组我常用的测试用例基本覆盖了所有零的位置。输入期望输出常见错误输出0零元整空输出1壹元整壹元少“整”10壹拾元整拾元整漏“壹”100壹佰元整壹佰零元整1001壹仟零壹元整壹仟零零壹元整1010壹仟零壹拾元整壹仟零壹拾零元整10000壹万元整壹万零元整10001壹万零壹元整壹万壹元整100001000壹亿零壹仟元整壹亿壹仟元整100000001壹亿零壹元整壹亿元零壹元整多“元”100010001壹亿零壹万零壹元整壹亿壹万零壹元整123456789壹亿贰仟叁佰肆拾伍万陆仟柒佰捌拾玖元整少“亿”或多“零”我调试时的习惯是先盯“零”的位置把所有非零数字先忽略。这题90%的BUG都出在零上连续零、节前零、整节为零、末尾零。你把这四种情况跑熟了剩下的机械拼接反而不容易错。还有一个经验别只拿题目的三个样例试完就交。PTA判题是拿一堆隐藏用例跑你永远猜不到它会卡哪一位。我建议自己写个小循环从0到999999999全部输出到文件里再去抽查几个容易出问题的数。如果担心眼睛看不过来就随机生成几千个数和一份正确参考实现的输出做diff哪一行不一样就直接定位到具体数字和出错的组。5. 如果打算用Python写把同一套逻辑翻译过去PTA现在很多题目也支持Python虽然运行效率不如C但这类输出题完全够用。Python版本思路完全一致只是把printf换成字符串拼接。num [零, 壹, 贰, 叁, 肆, 伍, 陆, 柒, 捌, 玖] wei [, 拾, 佰, 仟] def section(x): res [] d [x // 1000, x // 100 % 10, x // 10 % 10, x % 10] start 0 while start 4 and d[start] 0: start 1 if start 4: return zero False for i in range(start, 4): if d[i] 0: zero True else: if zero: res.append(零) zero False res.append(num[d[i]]) res.append(wei[3 - i]) return .join(res) def convert(n): if n 0: return 零元整 yi n // 100000000 wan n % 100000000 // 10000 ge n % 10000 out [] gap False if yi 0: out.append(section(yi) 亿) if wan 0: if out and (gap or wan 1000): out.append(零) gap False out.append(section(wan) 万) elif out: gap True if ge 0: if out and (gap or ge 1000): out.append(零) out.append(section(ge)) return .join(out) 元整 n int(input()) print(convert(n))这个版本里out是一个字符串列表gap负责跨节补零本质上和C语言的has、gap是对应的。Python里写起来会更短但如果你要交的PTA是纯C题目还是用前面的C代码更稳妥。最后说一个我自己的习惯。做这种输出格式题先把题目样例手抄在纸上再用红笔把所有不该读的零圈出来把所有该读的零也圈出来最后才写代码。人的眼睛很容易被连续数字骗过去比如100001000盯着看半天可能都没注意到中间整节是零但一拆成亿、万、个三组结构立刻清楚。PTA判题对空白、换行、中文标点都很敏感提交前记得确认没有多空格、没有全角字符、没有漏掉末尾换行。币值转换这题本身不难难的是把中文读法翻译成状态标记只要零处理想明白了拿满分是很稳的。