☰
算法笔记练习2.2问题A:按要求输出信息为何总是WA?
2026/10/22 5:59:46 网站建设 项目流程

第一次刷到《算法笔记》练习第2.2节这道问题 A 时,我差点笑出声:没有输入、没有算法、没有边界条件,唯一要做的事情就是按要求输出一行信息。可就是这种题,初学群里总能见到各种让人大跌眼镜的 WA 记录——多一个空格、少一个换行、把英文感叹号打成中文全角,甚至有人因为输出内容和题目要求不完全一样而卡了半小时。

作为一个照着这本书把练习一章一章刷完的过来人,我想把这道“开胃题”彻底讲清楚。它看着简单,实际上藏着一个新手最容易忽视的关键点:在线测评系统比对的是字符,不是意思。这篇记录会从题目定位、判题原理、标准写法、常见翻车现场四个角度展开,最后分享两个我现在每道题都还在用的本地调试习惯。不管你是刚接触 C/C++ 的初学者,还是准备计算机考研机试、算法竞赛入门,这篇都值得读完再动手提交。

1. 这道“开胃题”到底在练什么

1.1 先看清楚题目本身的样子

我手上这个版本的问题描述很简单:输入部分没有任何内容,只需要输出一行指定信息。样例输出是这样的:

Dare to be the first!

这句话的意思是“敢为天下先”,也是很多老题库里非常经典的第一道输出题。不同平台或题库镜像里的输出文案可能略有差异,有的版本要求输出Hello World!,有的要求输出This is my first program!,所以动手前一定要先看你打开的那个题目页面上“输出样例”给出的具体内容,不要凭记忆默写。

这题的题号也很有意思,叫“例题1-1-1”。很多第一次接触的人会疑惑:我明明在第2章练习里,为什么题号是1开头?其实这是题库从纸质教材配套习题迁移过来时保留的旧编号体系,1-1-1大致对应“第1章第1节第1题”的位置,和当前所在的练习小节并不严格一致。这类命名在早期 OJ 里很常见,不需要过度解读,把它理解成一个“题目坐标”就好。

1.2 它训练的不是“输出”,而是“提交闭环”

这种题的核心考点绝不是 printf 怎么写,因为语法翻两页书就能学会。它真正要练的是第一次完整走通“写代码—编译—提交—看评测结果—修正—再提交”的闭环。

很多初学者在前面看书的时候,代码都是在本地编译器里跑一下就完事,跑出了结果就觉得自己会了。但在线测评完全不是这个逻辑:你写的程序要在评测服务器上重新编译,在隔离环境中运行,输出结果被收集起来和标准答案比对。哪怕本地运行得再好,只要评测系统认为格式不对,依然算错。

所以我把这道题看作“入坑第一课”。它用最小的代价帮你理解:程序不是写给自己看的,是写给评测系统判定的。这个认知越早建立,后面学选择结构、循环、数组时就越少走弯路。

2. 从提交到判题:一次提交背后的输出比对机制

2.1 所谓“正确”,是逐字符比对的结果

我见过不少同学提交后看到 Wrong Answer 第一反应是“我运行结果明明对的啊”。问题就出在“明明”两个字上:你看到的“对”和人眼判断的“对”,和评测系统认定的“对”不是一回事。

绝大多数传统 OJ 的做法是:把你的程序输出保存成一份文本,再和评测端预先准备好的标准输出文本做比对。比对的粒度是字符,不是“语义”。空格、制表符、换行,在比对时都是实实在在的字符,一个都不能多,一个也不能少。

也就是说:

  • 多一个空格,哪怕肉眼完全看不出来,会判 WA;
  • 少一个换行,导致两行内容挤在一起,会判 WA;
  • 字符串里的引号写成了中文全角引号,会判 WA;
  • 末尾多输出一个空行,某些系统容忍,某些系统直接判错。

这种严格比对在刚接触时让人很不适应,却是所有在线评测的共同底层规则。它像一个非常较真的老师,只看卷面上的最终答案,不看你的解题过程有多辛苦。

2.2 样例输出里看不见的\n

这块是新手最大的盲区。题目页面上显示的样例输出,是经过浏览器渲染之后的视觉效果,而不是文本文件里的原始内容。比如:

Dare to be the first!

看起来就是一行文字,但文本层面它通常以\n(换行符)结尾。也就是说,标准输出文件里实际存的是:

Dare to be the first!\n

这个结尾的换行符在页面上看不见,很多人就会漏写。漏掉之后,本地运行也看不出任何异常,因为终端照样显示一行文字,但评测端比对文件时,就会发现你的输出末尾少了一个\n字符。

那是不是所有系统都严格要求末尾换行?据我观察,不同 OJ 实现不太一样:有的在比对前会做行尾空白剥离,对末尾换行比较宽容;有的则直接做整文件比较,少一个字符就是 WA。安全做法永远只有一个:严格按照样例输出,末尾保留一个换行。不要赌评测系统的宽容度。

2.3 用一个极小实验理解“字符级比对”

如果你还不太理解“多一个空格为什么算错”,在本地做个简单实验就明白了。把样例输出保存成expected.txt,内容这样写:

Dare to be the first!

然后再建一个mine.txt,内容是:

Dare to be the first!

注意第二行的感叹号后面有一个空格。用命令比较:

diff -u expected.txt mine.txt

diff 会立刻指出两个文件不一致。这里没有任何“它差不多就是一样的”余地,文本比对就是这么严格。我们后面还会用到这个命令来排查问题。

3. printf、puts 与流式输出:三种写法的取舍细节

3.1 最标准的 printf 写法

这道题最推荐、也最贴合《算法笔记》风格的写法是:

#include <cstdio> int main() { printf("Dare to be the first!\n"); return 0; }

printf 输出字符串时,\n表示换行,必须手动写。这里的\n是反斜杠加字母 n 组成的转义序列,不要把它理解成“字母 n 前面加个斜杠”,它是换行符在 C/C++ 字符串里的统一表示。

很多初学者会问:return 0;到底要不要写?我的建议是要写。它表示程序正常结束并向操作系统返回退出码 0。虽然很多编译器不写也能过,但养成写return 0;的习惯,在以后写多分支、多函数时能避免不少奇怪的运行时问题。

3.2 puts 写法:自动补换行,省心也容易踩坑

第二种写法更简短:

#include <cstdio> int main() { puts("Dare to be the first!"); return 0; }

puts和printf的区别在于:puts 会自动在字符串末尾追加一个换行符。所以上面这种写法实际输出的是Dare to be the first!\n,正好满足要求。

但这里有一个非常常见的坑:如果有人在 puts 的字符串里又手写了一个\n:

puts("Dare to be the first!\n");

实际输出就会变成:

Dare to be the first!

也就是目标内容后面多了一个空行。这个在本地终端里看起来不明显,但放到评测系统里就可能导致 WA。后面第四节我会再详细展开这个案例。

3.3 C++ 流式输出:cout 与 endl 的真相

如果用 C++ 风格写,可以这样:

#include <iostream> using namespace std; int main() { cout << "Dare to be the first!" << '\n'; return 0; }

很多教材会教cout << endl;,endl的作用是输出换行并刷新输出缓冲区。就这一道题而言,endl和'\n'效果几乎一样,但后续只要涉及大量输出,endl会因为频繁刷新缓冲区而明显更慢。所以尽早养成用'\n'的习惯,对刷题生涯只会有好处。

三种写法对比一下:

写法是否自动换行适用场景注意点
printf否,需手动写\n后续要格式化输出整数、浮点数时转义序列别拼错
puts是,自动补一个换行只输出纯字符串字符串里别再写\n
cout否,需加endl或'\n'C++ 流式风格大量输出优先用'\n'

选哪种?考虑到这本书整体以 C 风格语法为主线,后面的输入、字符串处理也大量用printf/scanf体系,我建议把 printf 作为主用写法,同时知道 puts 的存在。这样既贴合教材节奏,也能在面对不同题目时灵活切换。

4. 我见过的翻车现场:九成新手都卡在这三个地方

4.1 感叹号后面多了一个空格:肉眼看不见的 WA

我认识的一位同学在群里发过这道题的 WA 截图,他非常困惑:自己明明做的就是“按要求输出信息”,程序跑出来的结果和题目样例一模一样,怎么提交就错?

后来他贴出代码字符串,我一眼就发现了问题:他的代码写的是:

printf("Dare to be the first! \n");

注意感叹号和\n之间多了一个空格。这个空格在终端显示时完全看不出来,但在评测系统的文本比对里就是一个多出来的字符。

完整的排查链路应该是这样的:

  1. 先把程序输出重定向到文件:./a.out > my_output.txt;
  2. 用cat -A my_output.txt查看原始文本,空格会显示为^I或可见标记,行尾会显示为$;
  3. 对比题目样例输出,重点看行尾和字符串内部;
  4. 删除多余空格后重新运行、重新提交。

这条链路我现在每道题都在用。不要相信自己的眼睛,要相信能显示“不可见字符”的工具。

4.2 英文感叹号打成中文全角感叹号

另一个高频翻车点是标点符号的中英文混用。题目要求输出的是半角英文感叹号!,有些同学在中文输入法状态下打出了全角感叹号!。这两个字符在屏幕上看起来非常接近,但在计算机里是完全不同的编码:英文感叹号在 ASCII 表里是 0x21,一个字节;中文全角感叹号在 UTF-8 编码下占三个字节。

我曾亲眼见过有人因为这个全角字符卡了整整一晚上,反复提交七八次都是 WA,最后把字符串里的字符复制到十六进制查看器里,才发现问题出在一个“长得差不多”的符号上。

排查思路很直接:

  1. 不要手动敲输出文本,直接从题目页面的“输出样例”里复制那行文字;
  2. 粘贴到代码的字符串中;
  3. 用编辑器的“显示空白字符”或“显示 Unicode 字符”功能检查;
  4. 确保字符串里所有标点都是半角英文状态下的符号。

这道题之后,凡是输出类题目,我基本都采用“先复制样例,再粘贴进代码”的方式,彻底绕过手误。

4.3 用 puts 又手写\n,白白多出一行

这个坑前面提到过,它的隐蔽性在于本地终端基本看不出来。比如:

puts("Dare to be the first!\n");

虽然输出终端上会显示一个空行,但很多人会主观忽略它,觉得“多一个空行没关系”。问题恰恰是:评测系统判定的是文本文件内容,不是终端视觉效果。

如何复现和确认?

./a.out | xxd

在十六进制输出里,你会看到目标字符串后面出现了两个0a换行字节,而标准答案只有一个。修复方法很简单:把字符串里的\n删掉,让 puts 自己补一个换行;或者改用 printf,手动控制唯一的换行。

顺着这个案例,我可以给出一个通用原则:凡是涉及输出的题目,提交前先检查输出文件的行数和末尾换行数量。方法可以用:

cat -A my_output.txt

它能把每一行结尾用$标记出来,空行一眼就能发现。

4.4 把“按要求输出”误解成“自由发挥输出”

还有一种翻车不太一样,不是格式问题,而是理解问题。题目叫“按要求输出信息”,有些同学没细看样例,直接输出了自己熟悉的Hello World!,然后自信提交,结果 WA。这其实不是不会写代码,而是没养成“先读题、再复制样例、最后写代码”的顺序。

在线评测世界里,程序的输出必须和标准答案完全一致,没有“意思对了就行”的余地。你写Hello World!表达的心情再好,也不是题目要的那句话。所以每次打开一道题,第一件事永远是看“输出样例”,并且把它当成金标准。

我建议的做法是:先把样例输出复制到代码注释里,再照着注释写输出。比如:

// 期望输出: // Dare to be the first! printf("Dare to be the first!\n");

这个小习惯可以帮你避免大量因为“看错题”而白白浪费的提交次数。

5. 从这题延伸出去:两个值得现在就养成的习惯

5.1 建立自己的最小提交模板

刷题和写业务代码不一样,每道题都在一个只有几行代码的框架里打转。与其每次新建文件重新敲int main(),不如从一开始就固定一个最小模板:

#include <cstdio> int main() { // 题目逻辑写在这里 return 0; }

需要 C++ 流式操作时,就换成:

#include <iostream> using namespace std; int main() { // 题目逻辑写在这里 return 0; }

这两个模板就是我刷题时的地基。每次开新题,先把模板复制过去,再往main函数里填内容。它带来的好处是:你永远不会因为忘记#include或漏写return 0这种低级错误浪费一次提交。

这里我也想多说一句:不要小看这种“格式纪律”。第2章的练习体验不会每次都像这样只有输出,后面 scanf、printf 的组合、选择结构的分支、循环结构的边界,会让代码迅速变长。如果一开始没有一个干净的模板,后面写出来的代码很容易出现括号乱飞、include 缺失的问题。

5.2 本地输出对比:用 diff 代替肉眼

我在前面多次提到diff命令,这里把它正式推荐给你。做输出类题目时,最可靠的本地方案是:

  1. 把题目样例输出保存为expected.txt;
  2. 本地编译运行程序,把输出重定向到my_output.txt:
    ./a.out > my_output.txt
  3. 用 diff 对比两个文件:
    diff -u expected.txt my_output.txt

如果 diff 没有任何输出,说明两个文件完全一致,这道题的输出部分基本稳了。如果 diff 显示了一堆<和>,那就说明两个文件存在差异,要仔细看差异在哪一行、哪一个字符。

甚至可以更紧凑一点,用进程替换直接对比,不生成中间文件:

diff -u <(./a.out) expected.txt

这个习惯帮我避开了太多本可以避免的 WA。尤其在多行输出的题目里,第1行对、第2行多了一个空格,肉眼很容易忽略,但 diff 会明明白白地把差异列出来。

5.3 关于这道题本身,我最后再补充一句

现在回头看,这道“按要求输出信息(1)”确实是我刷题生涯里最没有“含金量”的一道题,但它带给我的收获一点都不小。它让我第一次意识到:在线评测系统面前,程序输出的每一个字符都在被严格审视,所谓的“差不多”就是“差很多”。

这道题之后,我开始养成提交前检查输出文件、用 diff 做本地比对、从样例复制而不是手打输出文本的习惯。这几个习惯在后来的选择结构、循环、数组题目里,帮我省下了不知道多少时间和提交次数。

如果你刚走到这题前面,别急着点提交,先在本地把输出内容、空格、换行都检查一遍,再进入下一题。这样,你的“算法笔记练习记录”就从第一题开始,踩在了最稳的地面上。

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

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

立即咨询