公司动态
NOI题库1.1输入输出全题解:C++代码与避坑指南
简介面向NOI及信息学入门选手的1.1编程基础之输入输出章节题解包覆盖Hello World、整数/浮点数输出、对齐输出、字符图形与游戏动画等10道经典入门题。每道题均提供可直接提交的C源码代码采用基础语法与常用头文件风格简洁便于初学者对照理解输入输出格式控制、输出宽度与小数位精度等核心知识点同时熟悉OJ评测环境。资源包共10个文件全部为.cpp源文件总体积仅4KB轻量便携支持Dev-C、VS Code、Code::Blocks等常见环境解压后即可按题号顺序浏览。目前已有5107人学习使用是备考NOI、刷OpenJudge题库时的高性价比参考。题解按题号逐个编号文件命名直观方便按照从易到难梯度练习与查漏补缺也可作为日后编写基础算法的代码模板。 你可能在NOI题库里翻到过1.1这个章节满屏都是“Hello, World!”“输出第二个整数”这样的名字第一反应多半是这也太基础了直接跳过。但我把1.1完整刷完之后可以负责任地说这一节的价值根本不在题目本身而在它帮你把“和评测机打交道”这件事彻底捋顺了。这篇内容把我整理的1.1全题解、完整C代码还有刷题时踩过的坑一次性放出来不管是刚摸到键盘的新手还是带学生的教练都能直接拿去用。1. 先弄明白1.1到底在考什么很多人觉得输入输出题没什么好讲的cin加cout走天下。但NOI这套题库的1.1“编程基础之输入输出”真正想训练的是三件事读入数据的准确性、输出格式的规范性、以及代码框架的稳定性。后面1.2、1.3的题目再复杂本质上仍然是在这三件事上叠加运算和逻辑。1.1 这一节的真实定位NOI题库的章节编排是有讲究的1.1是整个题库的起点它故意把题目出得特别“傻”——不考算法、不考思维甚至不考你知不知道循环。它只考一件事你能不能严格按照题目的输入输出要求把数据读进来再原样或格式化地送出去。为什么这样设计因为竞赛评测系统是逐字符比对的多一个空格、少一个换行、小数点位数不对都是零分。我见过太多选手算法思路全对最后挂在输出格式上那种感觉比不会做还难受。1.1就是用来提前消灭这种低级失误的。所以别嫌它简单你在这个章节养成的格式敏感度会一直影响到后面的字符串、结构体、动态规划。1.2 刷题前必须知道的两条评测规则第一条NOI系列比赛和题库默认使用C代码必须包含主函数并且正常返回0。有些同学喜欢写void main这在某些评测机上会直接编译错误。第二条评测系统的输出比对是“全文比对”你输出的每一个字符都会和标准答案逐字对照。行尾多余的空格、最后少一个换行都会被判为错误答案。另外虽然1.1的题用cin、cout完全没问题但我建议从这一节开始就习惯用scanf和printf。原因很简单竞赛里很多题目的数据量在十万甚至百万级别cin、cout如果不做任何优化性能会比scanf、printf差不少。提前养成用C风格输入输出的习惯后面会省很多事。2. 1.1逐题全解完整代码与思路这一节我把1.1里常见的九道题全部过一遍。每一题都给出可以直接提交的C代码并解释关键点。不同版本的题库在题号上有细微差别但核心考点是同一套东西你对照题目名字找就行。2.1 第1-3题Hello World、输出第二个整数、对齐输出第1题“Hello, World!”是整个题库的起点没有任何技术含量就是让你走通“写代码-编译-提交-通过”这条流程。代码非常简单#include iostream using namespace std; int main() { cout Hello, World! endl; return 0; }这里要注意的是输出内容的大小写和标点必须和题目样例完全一致一个字符都不能差。有些同学喜欢顺手在末尾加个空格或者把World写成world都会WA。第2题“输出第二个整数”题目是读入三个整数然后输出第二个整数。这道题开始涉及“读入多个数据”了很多人会在这里第一次体会到scanf的格式控制#include cstdio int main() { int a, b, c; scanf(%d%d%d, a, b, c); printf(%d\n, b); return 0; }三个%d之间没有空格也没有逗号因为scanf在读入整数时会自动跳过空白字符。你输入“1 2 3”或者“1回车2回车3回车”效果完全一样这算是以后读入大量数据时的一个基础认知。第3题“对齐输出”题目要求读入三个整数每个整数按8个字符宽度右对齐输出。这是第一次真正意义上考“格式”的题#include cstdio int main() { int a, b, c; scanf(%d%d%d, a, b, c); printf(%8d %8d %8d\n, a, b, c); return 0; }%8d的含义是如果整数不足8位用空格在左边补齐让数字整体右对齐。比如123输出就是“五个空格加123”。如果数字超过8位就按实际位数输出不会被截断。这里还有一个隐藏考点三个数之间要求用空格分隔所以格式串中间写了空格但这个空格不计入8位宽度是单独的一个分隔符。我见过有同学在这里纠结了很久其实自己跑一次样例就明白了。2.2 第4-7题浮点数输出的四种姿势第4题“输出保留3位小数的浮点数”输入一个单精度浮点数保留3位小数输出。这道题引入了printf里最常用的精度控制%.3f。注意读入float用的是%f输出也是%f只不过中间加了一个“ .3 ”表示保留3位小数。#include cstdio int main() { float f; scanf(%f, f); printf(%.3f\n, f); return 0; }第5题“输出保留12位小数的浮点数”是第4题的加强版输入换成双精度浮点数double输出保留12位小数。关键区别在于scanf读double必须用%lf而printf输出double用%f就可以。这个细节每年都有大量新手踩坑。#include cstdio int main() { double d; scanf(%lf, d); printf(%.12f\n, d); return 0; }为什么第5题要换成double因为float的有效数字大约只有7位你就算让printf输出12位小数后面几位也是不准确的噪声数据。double的有效数字大约是15位才能撑起12位小数的精度。所以以后凡是题目里出现“保留多位小数”或者“精度要求高”优先考虑double。第6题“空格分隔输出”是1.1里第一个真正的坑题。它要求读入一个字符、一个整数、一个单精度浮点数、一个双精度浮点数然后按顺序用空格分隔输出。#include cstdio int main() { char c; int a; float f; double d; scanf( %c %d %f %lf, c, a, f, d); printf(%c %d %f %lf\n, c, a, f, d); return 0; }注意看scanf的第一个格式是“空格%c”这个空格极其关键。因为%c不会像%d、%f那样自动跳过空白字符如果你直接写scanf(%c%d, c, a)那么%c读到的很可能上一个输入遗留在缓冲区里的换行符。加上这个空格就是告诉scanf“跳过所有空白字符再读一个字符”。这个问题会贯穿整个竞赛生涯如果你发现读入的字符永远是换行或者空格先检查这里。第7题“输出浮点数”是一道纯格式题要求读入一个双精度浮点数分别按%f、%e、%g三种格式输出。%f是普通小数形式%e是科学计数法%g是让系统在%f和%e里自动挑选更短的那个。#include cstdio int main() { double d; scanf(%lf, d); printf(%f\n%e\n%g\n, d, d, d); return 0; }这道题几乎没有算法含量考的就是你知不知道这三个格式符的存在。很多从Python或其他语言转过来的同学会在这里卡一下因为其他语言的格式化输出没有这么细的区分。2.3 第8-9题字符三角形与字符菱形第8题“字符三角形”输入一个字符输出由这个字符拼成的三行三角形第一行1个字符居中第二行3个第三行5个。这是图形输出类题目的第一个代表。#include cstdio int main() { char c; scanf( %c, c); printf( %c\n, c); printf( %c%c%c\n, c, c, c); printf(%c%c%c%c%c\n, c, c, c, c, c); return 0; }这种题直接printf最直观但如果你想练循环也可以写成下面的形式这个在第9题里更实用第i行字符数是2*i-1前置空格数是3-i。第9题“字符菱形”是第8题的升级版输入一个字符输出由1、3、5、3、1个字符组成的菱形。这题如果还用手写五行printf效率很低更关键的是如果以后遇到20行的图形题手写完全不现实。所以从这道题开始必须建立“找规律、用循环”的思维方式。#include cstdio int main() { char c; scanf( %c, c); for (int i 1; i 3; i) { for (int j 0; j 3 - i; j) printf( ); for (int j 0; j 2 * i - 1; j) printf(%c, c); printf(\n); } for (int i 2; i 1; i--) { for (int j 0; j 3 - i; j) printf( ); for (int j 0; j 2 * i - 1; j) printf(%c, c); printf(\n); } return 0; }上半部分是1到3行下半部分是2到1行。关键规律就是两个前置空格数随着行数增加而减少字符数随着行数增加而增加。你把这两个数用行号i表示出来剩下的就是循环嵌套。图形输出类题目在后面的竞赛里会反复出现比如打印菱形、打印沙漏、打印杨辉三角本质上都是同一套“找规律”的方法。3. 把1.1串起来训练计划(plan)实战刷完前面这些基础题我建议你找一道综合题来检验一下。题库里有一道叫“训练计划(plan)”的题题干大意是为了在NOI比赛中取得好成绩小K准备在n天时间内完成一个训练计划每天安排一定数量的题目最后需要统计整个计划的总题数和平均每天的题量。3.1 题目描述与考点拆解这道题表面上是个应用题其实考点非常集中第一读入天数n第二用循环读入n个整数并累加第三计算平均值并保留两位小数第四处理整数除法的精度问题。它把前面1.1里“读入多数据”“格式化输出”的知识点和后面循环结构的基础能力串在了一起非常适合作为1.1章节的收尾练习。我按最常见的描述来设计第一行输入一个整数n表示训练天数第二行输入n个整数表示每天完成的题目数。输出一行先输出总题数再输出平均每天完成的题数保留两位小数中间用空格隔开。3.2 核心代码与容易忽略的地方参考代码如下#include cstdio int main() { int n; scanf(%d, n); int sum 0, x; for (int i 0; i n; i) { scanf(%d, x); sum x; } printf(%d %.2f\n, sum, (double)sum / n); return 0; }这里最容易踩的坑是平均值计算。sum和n都是int直接写sum / n会触发整数除法比如5除以2会得到2而不是2.5。解决办法是先把sum强转为double再除以n这样整个表达式的类型就变成double了。另一个容易忽略的点是输出格式里的%.2f它对应平均值这个double如果你忘了保留两位小数输出会和样例对不上甚至格式错误。如果你拿到的题目版本要求输出每天的具体安排比如“Day 1: xx题”这样的格式那就把循环里的sum累加改成一行printf输出再单独统计总题数。思路是一样的核心还是读入和格式化输出的基本功。4. 新手踩坑实录这些错我几乎每次都能遇到刷1.1的时候很多错误不是你不会做而是“你以为你做对了”。我整理了几个出现频率最高的问题按排查顺序列出来下次WA了直接对照检查。4.1 常见问题速查表现象原因解决办法读入字符时得到的是换行或空格%c不跳过空白字符读到了缓冲区残留的换行scanf( %c, c)在%c前加一个空格输出结果变成科学计数法用cout输出浮点数但没指定fixed和setprecision改用printf或者写cout fixed setprecision(位数)输出和样例一模一样仍然WA行尾多了空格或者最后少了一个换行检查printf格式串行尾不要有多余字符scanf读double后值不对用了%f而不是%lfdouble类型读入必须用%lf平均值永远是个整数int除以int被截断先转double再除比如(double)sum / n编译不通过报一堆奇怪错误用了C11新特性而题库用的编译器版本较老写代码时尽量用C98/11兼容写法比如数组大小用常量4.2 格式化输出与读入的几个隐藏细节关于行尾空格我再强调一次。很多人写代码喜欢在循环里输出“数字加空格”最后一位后面也带个空格。人眼看不出区别评测机看得出来标准答案末尾没有空格你多了个空格就是WA。1.1的题目数据量小影响不明显但这个习惯一定要尽早改。关于多组数据读入后面很多题会写成“输入多组数据直到文件结束”比如while (scanf(%d, n) ! EOF)。这个写法在1.1里没出现但如果你提前掌握后面会轻松很多。scanf的返回值是成功读入的变量个数读到文件末尾时返回EOF也就是-1。这就是“边读边处理直到读完”的通用套路。关于main函数我建议永远写int main()并在最后return 0。这是标准写法任何评测机都不会找麻烦。有些编译器接受void main但在竞赛环境里这不是一个值得赌的行为。5. 1.1刷完之后下一步怎么走刷完1.1你已经完成了竞赛路上的第一小步。这一小步虽然技术含量不高但意义不小因为你第一次完整地走通了“读题、写代码、提交、通过”的闭环。下一步的安排我建议按节奏来别急着冲刺高难度算法。5.1 从输入输出到算法入门的路线NOI题库的章节顺序本身就是一个合理的训练路线。1.1输入输出之后接下来是1.2变量定义与运算、1.3顺序结构、1.4分支结构、1.5循环结构。我建议用两周左右的时间把1.1到1.5全部刷完每天保持10到20题的节奏。这个阶段不需要搞题海战术关键是每一道题都要亲手写一遍、提交通过不要眼高手低。刷完基础输入输出和流程控制之后再进入数组和字符串。到时候你会发现所有复杂程序的外壳其实还是1.1练过的那些东西读入数据、处理、按格式输出。区别只是中间的处理逻辑变复杂了。5.2 给训练计划加一点变化如果你觉得1.1太简单可以试着把之前那道“训练计划(plan)”做个小小的变式读入每天的题量不仅要输出总题量还要找出最多的一天和最少的一天。这只需要在循环里加两个判断但你会开始接触“维护最值”这种算法思维的雏形。#include cstdio int main() { int n, x, sum 0, maxv 0, minv 1000000; scanf(%d, n); for (int i 0; i n; i) { scanf(%d, x); sum x; if (x maxv) maxv x; if (x minv) minv x; } printf(%d %d %d\n, sum, maxv, minv); return 0; }这个变式看似只是加了两行if实际上你已经从“格式正确”跨到了“会处理数据”的阶段。后面学数组、学排序、学二分都是在这样的基础上一点一点垒起来的。我个人的习惯是不管学生多急着往后赶都要在1.1停留一天把每道题用printf和cout各写一遍再手写一遍输出格式。听起来像重复劳动实际上这是很多人后来WA在格式上的解药。最后分享一个小技巧刷1.1的时候把所有代码单独存成一个“输入输出模板”文件夹后面学数组、学字符串、学结构体每次写新程序都从模板复制基本框架能省下很多低级错误。本文还有配套的精品资源点击获取