文章

信奥赛NOI编程入门例题拓展精讲1

判断某一年某个月有几天

前言

本文目的:

  • 借用一些常见的入门练习题,复习和拓展《初等数论和算法入门》的知识

  • 顺带,熟悉 NOI 的“阉割+嫁接版 C/C++”

  • 同时,对比掌握真正的C/C++,兼容NOI

本文背景

  • 2024年寒假,为帮助孩子和几位同学自学并更好掌握 Python,龙爸编写了《初等数论和算法入门》的辅导材料。让孩子们在自学的基础上,通过一些精讲的题目来掌握初等数论的知识。
    ——龙爸给孩子们找了很多书籍、视频、教材,但都不满意。专业的不适合孩子,适合孩子的又太业余。说真的,NOI/CSP行业,包括洛谷等很多教材、题目问题太多。

    甚至NOI的某些比赛题里的毛病,经常让人哭笑不得——草台班子。

    ——龙爸认为:教、学编程,应该融入《初等数论》的基础知识和算法思维,不该分割开。

  • 2025年寒假,学校请的培训机构时不时在群里发了一些入门练习,

    刚好我们可以用来熟悉 NOI 的“阉割+嫁接版 C/C++”,复习和拓展《初等数论和算法入门》的知识。

龙爸强调:我们学编程不是为了信奥赛,不是等级考试和CSP,而是:

  1. 锻炼“思维”,分析和解决问题的方法

  1. 提高学习效率——必须在高考前学会提高学习效率,越早越好。

龙爸家的规则:如果学习效率高,有兴趣可以参赛,否则学习为主。

不考虑特招、降分——舍本逐末、因果颠倒。

而且,参加奥数才是本事,信奥可以考虑顺手。

所以,既然意外进了信奥队、不得不分一部分精力 熟悉 C/C++,兼容NOI 的“阉割+嫁接版 C/C++”——要学就学正宗的C/C++,但要能读懂不正宗的代码,以及必要时兼容之

(我们硬件编程基本不用C,Micro Python、.NET/C# 都很好,以后考虑Rust)

这个系列中,龙爸会坚持一贯的原则:

——设法多引导孩子从不同角度去思考,鼓励孩子实践

——自己提出问题,然后查阅资料,去设计实验来验证自己的疑问。

一些入门级的例题

接下来用跟着学校培训机构打卡遇到的一些入门题作为例题,进行拓展:

机构按进度教学很正常,没有贬低的意思,只是刚好遇到了就不浪费

1. 判断某一年某个月有几天

这位同学的回答,可以作为标准答案了,非常好。

(巧合的是,这位同学的父亲刚好是某985大学软件学院的老师,教授的课程包括《算法与数据结构》,可见——能教计算机专业大学生,却未必能教小学生中学生啊)

但是,如果我是老师,教的是我的儿子——不能浪费我儿子的时间。

所以,从一开始,我就会引导孩子们注意,避免发生下面的问题:

🧠

  1. 验证数据有效性:虽然题目有要求,输入的两个数字必须在指定范围内。

——但,我们要养成验证数据有效性的好习惯。

  1. 分散的多个 cout 输出,合并——引入一个变量,最后在统一的位置输出;

——因为,未来要理解“低耦合,高内聚,从细节开始打基础。

  1. 注意变量命名:用有意义的名字会写的单词尽量用单词,不会的可以学(尤其常用词,重点是多用),实在特别复杂的、不常用的可以用拼音代替——或者查询了抄下来。

且,Python 和 C-Like 语言的命名规则不同,这里用小驼峰命名(Lower Camel Case)。

另外引导孩子学习、思考:

🧠

  1. 拓展要求,换种思路:如果不允许用 if-else,如何实现?

  1. 拓展要求,熟悉C/C++自学三目运算符、bool 类型,用 bool 类型作为函数返回值。

下面是孩子的答案:

代码反而多了很多?——其实少了,只是写了两个不同的函数判断是否闰年 isLeapYear() 和 isLeapYear_Int() 做对比:

简单分析拓展部份要求:

  1. 用数组代替复杂判断——这是一种不同的思维方式和解决问题的技巧,未来会经常用到

int days = daysInMonth[month - 1];
// 而不是:
if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)
  1. 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最完美。

  1. 要注意:bool 类型的 true 和 false 虽然可以视为(“隐式转换”为) int 的 0 和 1,反之:

    1. int 的 0 被隐式转换为 bool 的 false

    2. int 的 除了 0 以外的任意值,被隐式转换为 bool 的 true,比如 -1 和 1 都对应 true。

——也就是说,隐式转换的时候,两个方向的取值范围有差异。千万注意!

这也是不推荐“隐式转换”的原因之一,因为不小心就会埋下一个大坑

License:  CC BY 4.0