信奥赛NOI编程入门例题拓展精讲1
判断某一年某个月有几天
前言
本文目的:
借用一些常见的入门练习题,复习和拓展《初等数论和算法入门》的知识
顺带,熟悉 NOI 的“阉割+嫁接版 C/C++”
同时,对比掌握真正的C/C++,兼容NOI
本文背景
2024年寒假,为帮助孩子和几位同学自学并更好掌握 Python,龙爸编写了《初等数论和算法入门》的辅导材料。让孩子们在自学的基础上,通过一些精讲的题目来掌握初等数论的知识。
——龙爸给孩子们找了很多书籍、视频、教材,但都不满意。专业的不适合孩子,适合孩子的又太业余。说真的,NOI/CSP行业,包括洛谷等很多教材、题目问题太多。甚至NOI的某些比赛题里的毛病,经常让人哭笑不得——草台班子。
——龙爸认为:教、学编程,应该融入《初等数论》的基础知识和算法思维,不该分割开。
2025年寒假,学校请的培训机构时不时在群里发了一些入门练习,
刚好我们可以用来熟悉 NOI 的“阉割+嫁接版 C/C++”,复习和拓展《初等数论和算法入门》的知识。
龙爸强调:我们学编程不是为了信奥赛,不是等级考试和CSP,而是:
锻炼“思维”,分析和解决问题的方法
提高学习效率——必须在高考前学会提高学习效率,越早越好。
龙爸家的规则:如果学习效率高,有兴趣可以参赛,否则学习为主。
不考虑特招、降分——舍本逐末、因果颠倒。
而且,参加奥数才是本事,信奥可以考虑顺手。
所以,既然意外进了信奥队、不得不分一部分精力 熟悉 C/C++,兼容NOI 的“阉割+嫁接版 C/C++”——要学就学正宗的C/C++,但要能读懂不正宗的代码,以及必要时兼容之。
(我们硬件编程基本不用C,Micro Python、.NET/C# 都很好,以后考虑Rust)
这个系列中,龙爸会坚持一贯的原则:
——设法多引导孩子从不同角度去思考,鼓励孩子实践
——自己提出问题,然后查阅资料,去设计实验来验证自己的疑问。
一些入门级的例题
接下来用跟着学校培训机构打卡遇到的一些入门题作为例题,进行拓展:
(机构按进度教学很正常,没有贬低的意思,只是刚好遇到了就不浪费)
1. 判断某一年某个月有几天
这位同学的回答,可以作为标准答案了,非常好。
(巧合的是,这位同学的父亲刚好是某985大学软件学院的老师,教授的课程包括《算法与数据结构》,可见——能教计算机专业大学生,却未必能教小学生中学生啊)
但是,如果我是老师,教的是我的儿子——不能浪费我儿子的时间。
所以,从一开始,我就会引导孩子们注意,避免发生下面的问题:
🧠
验证数据有效性:虽然题目有要求,输入的两个数字必须在指定范围内。
——但,我们要养成验证数据有效性的好习惯。
分散的多个 cout 输出,合并——引入一个变量,最后在统一的位置输出;
——因为,未来要理解“低耦合,高内聚”,从细节开始打基础。
注意变量命名:用有意义的名字,会写的单词尽量用单词,不会的可以学(尤其常用词,重点是多用),实在特别复杂的、不常用的可以用拼音代替——或者查询了抄下来。
且,Python 和 C-Like 语言的命名规则不同,这里用小驼峰命名(Lower Camel Case)。
另外引导孩子学习、思考:
🧠
拓展要求,换种思路:如果不允许用 if-else,如何实现?
拓展要求,熟悉C/C++:自学三目运算符、bool 类型,用 bool 类型作为函数返回值。
下面是孩子的答案:
代码反而多了很多?——其实少了,只是写了两个不同的函数判断是否闰年 isLeapYear() 和 isLeapYear_Int() 做对比:
简单分析拓展部份要求:
用数组代替复杂判断——这是一种不同的思维方式和解决问题的技巧,未来会经常用到
int days = daysInMonth[month - 1];
// 而不是:
if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)
C++引入了 bool 类型,并且bool 类型的 false 和 true可以被视为 int 的 0 和 1,所以可以这么写
这称为“隐式转换”——大部分情况下不推荐“隐式”编码
——针对大一点儿的孩子,请问为什么不推荐隐式转换?可以查阅资料深入了解):
int days = daysInMonth[month - 1]
if (m == 2)
days += isLeapYear(year);
上面的代码里,days 加 isLeapYear() 函数的返回值,用于修正“闰年的2月多一天”。
isLeapYear() 返回 true 意味着是闰年,则 days 加 true/1
否则 加 false/0
同理,isLeapYear_Int() 函数则是将条件表达式的值 true 或 false 隐式转换为 int 的 0 和 1。
这里是为了举例而故意“隐式转换”,否则函数直接返回 int 的 0、1最完美。
要注意:bool 类型的 true 和 false 虽然可以视为(“隐式转换”为) int 的 0 和 1,反之:
int 的 0 被隐式转换为 bool 的 false;
int 的 除了 0 以外的任意值,被隐式转换为 bool 的 true,比如 -1 和 1 都对应 true。
——也就是说,隐式转换的时候,两个方向的取值范围有差异。千万注意!
这也是不推荐“隐式转换”的原因之一,因为不小心就会埋下一个大坑。