commit 34bd44cf159919a7a07c0cf27f73d1e3050b2938 Author: chengzi Date: Thu Jul 2 16:54:05 2026 +0800 init: 12 lessons + outline diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..742ba49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# 忽略所有文件 +/* +/*/ + +# 保留以下内容 +!/GESP暑假班大纲.md +!/第01课-C++自带函数/ +!/第02课-循环结构/ +!/第03课-自定义函数/ +!/第04课-数据类型与分支结构/ +!/第05课-嵌套循环与图形打印/ +!/第06课-字符数组与string/ +!/第07课-数组计数法/ +!/第08课-枚举与模拟/ +!/第09课-GESP一级真题训练/ +!/第10课-GESP二级真题训练一/ +!/第11课-GESP二级真题训练二/ +!/第12课-模拟考试/ + +# 保留各课内的 .md 文件及子文件 +!*.md +!**/*.md +!.gitignore diff --git a/GESP暑假班大纲.md b/GESP暑假班大纲.md new file mode 100644 index 0000000..b43d502 --- /dev/null +++ b/GESP暑假班大纲.md @@ -0,0 +1,204 @@ +# GESP暑假集训班大纲 + +## 课程信息 +- **课程目标**:巩固GESP一级内容,备战9月等级考试,融入二级核心知识 +- **适合学生**:学过一年C++,基础较好 +- **课时安排**:12节课 × 2小时 +- **考试目标**:9月GESP一级全员通过,部分学生冲刺二级 + +--- + +## 课程大纲总览 + +| 节次 | 课程名称 | 等级 | 核心知识点 | +|:---:|----------|:---:|------------| +| 1 | C++自带函数 | 一级 | 标准库函数调用、头文件、字符判断函数 | +| 2 | 循环结构 + 数位拆解 | 一级 | for/while循环、数位拆解技巧、回文数判断 | +| 3 | 自定义函数 | 四级拓展 | 函数定义与调用、参数传递、返回值 | +| 4 | 数据类型与分支结构 | 一级 | 五种数据类型、ASCII码、if-else分支、打擂台 | +| 5 | 嵌套循环与图形打印 | 二级 | 嵌套循环执行流程、图形规律分析 | +| 6 | 字符数组与string | 二级 | string基本操作、字符串处理 | +| 7 | 数组计数法 | 二级 | 计数思想、频次统计、去重输出 | +| 8 | 枚举与模拟 | 三级工具 | 枚举思想、模拟算法 | +| 9 | GESP一级真题训练 | 冲刺 | 一级综合练习 | +| 10 | GESP二级真题训练一 | 冲刺 | 二级综合练习 | +| 11 | GESP二级真题训练二 | 冲刺 | 二级综合练习 | +| 12 | 模拟考试 | 冲刺 | 模拟考试环境、查漏补缺 | + +--- + +## 各课详细知识点 + +### 第1课:C++自带函数 +**等级**:一级 | **核心**:正确选择并使用标准库函数 + +| 知识模块 | 具体内容 | +|----------|----------| +| 数学函数 | `sqrt`(开平方)、`abs`(绝对值)、`pow`(幂运算) | +| 最值与交换 | `max`、`min`、`swap` | +| 字符判断 | `isalpha`、`isdigit`、`islower`、`isupper` | +| 字符转换 | `tolower`、`toupper` | +| 头文件 | ``、``、`` | + +**考点**:函数名、头文件、参数类型、返回值 + +--- + +### 第2课:循环结构 + 数位拆解 +**等级**:一级 | **核心**:循环边界与数位拆解 + +| 知识模块 | 具体内容 | +|----------|----------| +| 三种循环 | `for`(次数明确)、`while`(条件控制)、`do-while`(至少执行一次) | +| 数位拆解 | 取个位 `n%10`、去个位 `n/10`、求各位和、反转数字 | +| 回文数判断 | 反转后与原数比较 | +| 水仙花数 | 三位数各位立方和等于自身 | + +**核心口诀**:取个位、拼新数、去个位 + +--- + +### 第3课:自定义函数(拓展) +**等级**:四级拓展 | **核心**:函数的参数、返回值与调用过程 + +| 知识模块 | 具体内容 | +|----------|----------| +| 函数定义 | 返回类型、函数名、参数列表、函数体 | +| 参数传递 | 形参vs实参、值传递 | +| 返回值 | `return`语句、`void`函数 | +| 函数重载 | 同名不同参数 | +| 变量作用域 | 局部变量vs全局变量 | + +**典型题目**:判断素数函数 + +--- + +### 第4课:数据类型与分支结构 +**等级**:一级 | **核心**:数据类型、类型转换与数据范围 + +| 知识模块 | 具体内容 | +|----------|----------| +| 五种数据类型 | `int`、`char`、`double`、`bool`、`string` | +| ASCII码 | 字符本质是整数,`'0'`=48、`'A'`=65、`'a'`=97 | +| 类型转换 | 隐式转换、强制转换、`1.0*sum/n`触发浮点运算 | +| 分支结构 | `if`、`else if`、`else`、`switch`、三目运算 | +| 打擂台 | 普通打擂台、带并列规则的二次打擂台 | + +**核心口诀**:int存整数、char单字符、double小数、bool对错、string字符串 + +--- + +### 第5课:嵌套循环与图形打印 +**等级**:二级 | **核心**:嵌套循环中的行列关系 + +| 知识模块 | 具体内容 | +|----------|----------| +| 嵌套循环 | 外层管行、内层管列 | +| 直角三角形 | 左下、右下、左上、右上 | +| 等腰三角形 | 第i行:空格`n-i`个,星号`2i-1`个 | +| 菱形 | 正等腰 + 倒等腰 | + +**核心公式**: +- 等腰三角形星号数:`2i - 1` +- 前导空格数:`n - i` + +--- + +### 第6课:字符数组与string +**等级**:二级 | **核心**:字符串输入和逐字符处理 + +| 知识模块 | 具体内容 | +|----------|----------| +| 输入方式 | `cin >> s`(遇空格停)、`getline(cin, s)`(读整行) | +| 基本操作 | `s.length()`取长度、`s[i]`访问字符 | +| 查找替换 | `s.find()`、`s.replace()`、`s.substr()` | +| 字符统计 | 配合`isupper`、`islower`、`isdigit`分类统计 | +| 回文判断 | 首尾字符两两比较 | + +--- + +### 第7课:数组计数法 +**等级**:二级 | **核心**:数值映射为下标的计数思想 + +| 知识模块 | 具体内容 | +|----------|----------| +| 核心写法 | `cnt[x]++` | +| 频次统计 | 统计每个数字出现次数 | +| 最高频查找 | 打擂台找众数 | +| 去重升序输出 | 遍历计数数组,天然有序 | + +**适用条件**:数值范围有限,可作为数组下标 + +--- + +### 第8课:枚举与模拟 +**等级**:三级工具降维 | **核心**:枚举范围与模拟执行顺序 + +| 知识模块 | 具体内容 | +|----------|----------| +| 枚举思想 | 列举所有可能,逐一验证 | +| 枚举优化 | 缩小范围、提前终止 | +| 模拟思想 | 按题目描述一步步翻译成代码 | +| 典型题目 | 鸡兔同笼、找完数、斐波那契数列 | + +**枚举vs模拟**:枚举在"答案空间"中找,模拟在"时间步骤"中走 + +--- + +### 第9-11课:真题训练 +**目标**:综合运用所学知识,提高解题速度 + +| 训练内容 | 涵盖知识点 | +|----------|------------| +| 一级真题 | 输入输出、数据类型、分支、循环、数位处理 | +| 二级真题 | 数组、字符串、嵌套循环、计数法 | + +--- + +### 第12课:模拟考试 +**目标**:模拟考试环境,查漏补缺 + +| 内容 | 说明 | +|------|------| +| 考试环境 | 120分钟,100分,60分合格 | +| 时间管理 | 先易后难,留10分钟检查 | +| 检查清单 | 头文件、变量初始化、数组越界、输出格式 | + +--- + +## 核心考点速查表 + +| 考点类型 | 对应课程 | 关键技巧 | +|----------|----------|----------| +| 输入输出 | 1、4 | `cin/cout`、`getline` | +| 数据类型 | 4 | int/char/double的选择、ASCII码转换 | +| 分支结构 | 4 | if-else链、switch、打擂台 | +| 循环结构 | 2、5 | for/while选择、嵌套循环、图形规律 | +| 数组操作 | 7、9 | 遍历、计数、排序、去重 | +| 字符串处理 | 6 | string操作、字符判断、回文 | +| 函数 | 3 | 定义调用、参数返回值 | +| 算法思想 | 8 | 枚举、模拟 | + +--- + +## 打油诗口诀汇总 + +1. **自带函数**:自带函数真方便,头文件里全找见 +2. **自定义函数**:函数写好存身旁,起名调用响当当 +3. **数据类型**:int存整数范围广,char单字符本质藏 +4. **打擂台**:擂台之上比高矮,先请一位站上台 +5. **数位拆解**:循环取位倒着排,个位拼到新数来 +6. **回文数**:反转数字比原数,相等就是回文数 +7. **图形打印**:外层循环管行数,内层循环管列数 +8. **数组计数**:数值下标来对号,每次出现次数高 +9. **枚举模拟**:枚举就是全试过,模拟照着步骤走 + +--- + +## 参考资源 + +- **GESP官方网站**:https://gesp.ccf.org.cn +- **刷题平台**:https://www.luogu.com.cn(洛谷) +- **考试时长**:120分钟 +- **满分**:100分 +- **合格线**:60分 diff --git a/第01课-C++自带函数/教案-学生版.md b/第01课-C++自带函数/教案-学生版.md new file mode 100644 index 0000000..7b137d6 --- /dev/null +++ b/第01课-C++自带函数/教案-学生版.md @@ -0,0 +1,181 @@ +# 第1课:C++自带函数 + +## 教学目标 +- 学会查阅和使用C++标准库自带函数 +- 掌握 ``、``、`` 三个常用头文件 +- 能用自带函数解决计算问题,体会"站在巨人的肩膀上" + +## 核心考点:正确选择并使用标准库函数 + +### 考什么 +根据题目任务选择正确的________,同时写对________、参数和返回值。 + +### 必须理解 +调用函数要过三关:它属于哪个________;传入什么________;返回什么________。 + +### 核心写法 +```cpp +#include <________> // sqrt、pow +#include <________> // max、min、swap + +double ans = ________(x); +int ma = ________(a, b); +``` + +### 常见错误 +- `pow` 的返回结果能否一律当作精确整数?________________ + +### 判断是否掌握 +- [ ] 我能根据任务说出函数名和头文件。 +- [ ] 我能解释函数参数与返回值的含义。 +- [ ] 我能独立写出正确的调用语句。 + +--- + +## 函数速查表 + +### 一、数学计算函数(头文件:`______`) + +| 函数 | 参数 | 返回值 | 示例 | 说明 | +|------|------|--------|------|------| +| `sqrt(x)` | x — __________ | __________ — x 的平方根 | `sqrt(16)` → `___` | 开根号 | +| `abs(x)` | x — __________ | __________ — x 的绝对值 | `abs(-5)` → `___` | 去掉负号 | +| `pow(a, b)` | a — ______,b — ______ | __________ — a 的 b 次方 | `pow(2, 3)` → `___` | aᵇ | + +### 二、最值与交换函数(头文件:`______`) + +| 函数 | 参数 | 返回值 | 示例 | 说明 | +|------|------|--------|------|------| +| `max(a, b)` | 两个 __________ 的值 | __________ — 较大值 | `max(3, 7)` → `___` | 两数取大 | +| `min(a, b)` | 两个 __________ 的值 | __________ — 较小值 | `min(3, 7)` → `___` | 两数取小 | +| `swap(a, b)` | 两个 __________ 的**变量** | ________(无返回值) | `swap(x, y)` → __________ | 交换两变量的值 | + +### 三、字符判断函数(头文件:`______`) + +| 函数 | 参数 | 返回值 | 示例 | 说明 | +|------|------|--------|------|------| +| `isalpha(c)` | c — __________ | ____ — 是字母返回 `true` | `isalpha('A')` → `____` | 判断是否为字母 | +| `isdigit(c)` | c — __________ | ____ — 是数字返回 `true` | `isdigit('5')` → `____` | 判断是否为数字 | +| `islower(c)` | c — __________ | ____ — 是小写字母返回 `true` | `islower('a')` → `____` | 判断是否为小写 | +| `isupper(c)` | c — __________ | ____ — 是大写字母返回 `true` | `isupper('A')` → `____` | 判断是否为大写 | + +### 四、字符转换函数(头文件:`______`) + +| 函数 | 参数 | 返回值 | 示例 | 说明 | +|------|------|--------|------|------| +| `tolower(c)` | c — __________ | ____ — 对应的小写字母 ASCII 码 | `tolower('A')` → `___` | 大写转小写 | +| `toupper(c)` | c — __________ | ____ — 对应的大写字母 ASCII 码 | `toupper('a')` → `___` | 小写转大写 | + +--- + +## 课堂编程题 + +### 课堂1:三点中最近的一对【适中】 + +**题目描述:** 输入平面上三个点的坐标,求三对点之间的最短距离,结果保留两位小数。要求使用 `sqrt`、`pow` 和 `min`。 + +**输入格式:** 输入六个实数,依次表示三个点的横坐标和纵坐标。 + +**输出格式:** 输出最短距离,保留两位小数。 + +**数据范围:** 每个坐标的绝对值不超过 1000。 + +**输入样例:** +```text +0 0 3 4 0 2 +``` + +**输出样例:** +```text +2.00 +``` + +**解题思路:** 用勾股定理分别求出三段距离,再用 `min` 取最小值。 + +**易错提醒:** `sqrt` 返回浮点数;格式化输出需要包含 ``。 + +--- + +### 课堂2:整理三个数【适中】 + +**题目描述:** 输入三个整数,第一行输出最大值和最小值;再使用 `swap` 将三个变量调整为升序,第二行输出调整结果。 + +**输入格式:** 输入三个整数。 + +**输出格式:** 第一行输出最大值和最小值;第二行输出升序结果。 + +**数据范围:** `-10^9≤a,b,c≤10^9`。 + +**输入样例:** +```text +8 2 5 +``` + +**输出样例:** +```text +8 2 +2 5 8 +``` + +**解题思路:** 用嵌套的 `max`、`min` 求最值;再进行三次相邻比较,需要时调用 `swap`。 + +**易错提醒:** `swap` 的参数必须是两个变量,不能传入 `a+b` 这样的表达式。 + +--- + +### 课堂3:去掉最高分和最低分【适中】 + +**题目描述:** 输入若干个评委分数,去掉一个最高分和一个最低分,输出剩余分数之和。不得使用排序。 + +**输入格式:** 第一行输入评委人数;第二行输入所有评委给出的分数。 + +**输出格式:** 输出剩余分数之和。 + +**数据范围:** `3≤n≤100`,分数为 `0~100`。 + +**输入样例:** +```text +5 +9 7 10 8 6 +``` + +**输出样例:** +```text +24 +``` + +**解题思路:** 遍历时同时计算总分、最高分和最低分,最后用总分减去两个极值。 + +**易错提醒:** 最高分和最低分要用第一个合法分数初始化;即使最高分重复,也只去掉一个。 + +--- + +### 课堂4:字符身份证【适中】 + +**题目描述:** 输入一个可见字符。若为字母,输出 `LETTER` 和相反大小写的字符;若为数字,输出 `DIGIT`;否则输出 `OTHER`。 + +**输入格式:** 一个可见字符。 + +**输出格式:** 按题目要求输出。 + +**数据范围:** 标准 ASCII 可见字符。 + +**输入样例:** +```text +G +``` + +**输出样例:** +```text +LETTER g +``` + +**解题思路:** 用 `isalpha`、`isdigit` 分类,用 `islower` 或 `isupper` 判断大小写,再调用 `toupper` 或 `tolower` 转换。 + +**易错提醒:** `tolower` 和 `toupper` 返回的是字符编码,输出字符时应转换为 `char`。 + +--- + +## 课后作业 + +> 请完成本课对应的课后作业,见编程题文件。 diff --git a/第01课-C++自带函数/教案-教师版.md b/第01课-C++自带函数/教案-教师版.md new file mode 100644 index 0000000..092c3ac --- /dev/null +++ b/第01课-C++自带函数/教案-教师版.md @@ -0,0 +1,187 @@ +# 第1课:C++自带函数 + +## 教学目标 +- 学会查阅和使用C++标准库自带函数 +- 掌握 ``、``、`` 三个常用头文件 +- 能用自带函数解决计算问题,体会"站在巨人的肩膀上" + +## 核心考点:正确选择并使用标准库函数 + +### 考什么 +题目通常给出一个计算或处理任务,要求选择合适的标准库函数,并写对头文件、参数和返回值。例如开平方用 `sqrt`,求最大值用 `max`,判断字符用 `isdigit`。考试不只考“记住函数名”,还考能否把函数正确放进完整程序。 + +### 必须理解 +调用标准库函数要过三关:第一,函数属于哪个头文件;第二,传入参数的类型和含义;第三,返回结果是什么类型。例如 `sqrt(x)` 返回浮点数,即使 x 是整数,也要注意精度和类型。 + +### 核心写法 +```cpp +#include // sqrt、pow +#include // max、min、swap +#include // isdigit、isalpha + +double root = sqrt(x); +int bigger = max(a, b); +``` +先根据任务选函数,再补头文件,最后检查参数范围和返回类型。 + +### 常见错误 +- 忘记包含对应头文件,导致函数未声明。 +- 认为 `pow` 一定返回整数,直接用于要求精确整数的运算,可能受到浮点误差影响。 +- 只背函数名,不检查参数是否满足要求,例如对负数直接开平方。 + +### 判断是否掌握 +看到“开平方、绝对值、最值、交换、字符分类”时,能说出函数名、头文件、关键参数和返回结果,并能独立写出一段正确调用代码。 + +--- + +## 函数速查表 + +### 一、数学计算函数(头文件:``) + +| 函数 | 参数 | 返回值 | 示例 | 说明 | +|------|------|--------|------|------| +| `sqrt(x)` | `double x` — 一个非负数 | `double` — x 的平方根 | `sqrt(16)` → `4.0` | 开根号 | +| `abs(x)` | `int x` — 一个整数 | `int` — x 的绝对值 | `abs(-5)` → `5` | 去掉负号 | +| `pow(a, b)` | `double a` — 底数,`double b` — 指数 | `double` — a 的 b 次方 | `pow(2, 3)` → `8.0` | aᵇ | + +### 二、最值与交换函数(头文件:``) + +| 函数 | 参数 | 返回值 | 示例 | 说明 | +|------|------|--------|------|------| +| `max(a, b)` | 两个同类型的值 | 同类型 — 较大值 | `max(3, 7)` → `7` | 两数取大 | +| `min(a, b)` | 两个同类型的值 | 同类型 — 较小值 | `min(3, 7)` → `3` | 两数取小 | +| `swap(a, b)` | 两个同类型的**变量** | `void`(无返回值) | `swap(x, y)` → x和y交换 | 交换两变量的值 | + +### 三、字符判断函数(头文件:``) + +| 函数 | 参数 | 返回值 | 示例 | 说明 | +|------|------|--------|------|------| +| `isalpha(c)` | `char c` — 一个字符 | `bool` — 是字母返回 `true` | `isalpha('A')` → `true` | 判断是否为字母(A~Z 或 a~z) | +| `isdigit(c)` | `char c` — 一个字符 | `bool` — 是数字返回 `true` | `isdigit('5')` → `true` | 判断是否为数字(0~9) | +| `islower(c)` | `char c` — 一个字符 | `bool` — 是小写字母返回 `true` | `islower('a')` → `true` | 判断是否为小写字母(a~z) | +| `isupper(c)` | `char c` — 一个字符 | `bool` — 是大写字母返回 `true` | `isupper('A')` → `true` | 判断是否为大写字母(A~Z) | + +### 四、字符转换函数(头文件:``) + +| 函数 | 参数 | 返回值 | 示例 | 说明 | +|------|------|--------|------|------| +| `tolower(c)` | `char c` — 一个字符 | `int` — 对应的小写字母 ASCII 码 | `tolower('A')` → `97`(即 `'a'`) | 大写转小写 | +| `toupper(c)` | `char c` — 一个字符 | `int` — 对应的大写字母 ASCII 码 | `toupper('a')` → `65`(即 `'A'`) | 小写转大写 | + +> **注意**:`tolower` 和 `toupper` 返回的是 `int`(ASCII 码),输出时转回 `char`:`cout << (char)tolower('A');` 输出 `a`。 + +--- + +## 题目一:计算两点之间的距离 + +### 核心代码(一句话) +> 两点距离的核心:**勾股定理**,`sqrt` 和 `pow` 配合使用,一行搞定。 + +### 自然语言思路 +小明站在平面上的 A 点 `(x₁, y₁)`,小红站在 B 点 `(x₂, y₂)`,求两人之间的直线距离。 + +第一步,算出**水平方向**差了多少:`x₁ - x₂`。 +第二步,算出**垂直方向**差了多少:`y₁ - y₂`。 +第三步,把两个差值**分别平方**,平方就用 `pow(差值, 2)`。 +第四步,两个平方**加起来**。 +第五步,对和**开根号**,用 `sqrt`,得到最终距离。 +第六步,题目一般要求保留小数,用 `fixed << setprecision(2)` 保留两位。 + +> 其实就是把"勾股定理"翻译成代码:`c = sqrt(a² + b²)`。 + +### 伪代码 +``` +1. 输入 x1, y1, x2, y2 (四个浮点数) +2. 计算 dx = x1 - x2 (水平距离) +3. 计算 dy = y1 - y2 (垂直距离) +4. 计算 dist = sqrt( pow(dx, 2) + pow(dy, 2) ) +5. 输出 dist,保留两位小数 +``` + +### 真实代码 +```cpp +#include +#include // sqrt、pow 在这里 +#include // fixed、setprecision 在这里 +using namespace std; + +int main() { + double x1, y1, x2, y2; + cin >> x1 >> y1 >> x2 >> y2; + + double dist = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2)); + + cout << fixed << setprecision(2) << dist << endl; + return 0; +} +``` + +### 注意点 +1. **头文件别忘**:`sqrt` 和 `pow` 在 `` 里,格式化输出在 `` 里。 +2. **pow 的第二个参数**:`pow(底数, 指数)`,指数写 `2` 表示平方,写 `0.5` 也能表示开方(和 sqrt 等价)。 +3. **坐标相减顺序无所谓**:`pow(x1-x2, 2)` 和 `pow(x2-x1, 2)` 结果一样,因为平方消掉了负号。 +4. **保留小数的另一种写法**:`printf("%.2f", dist)` 也可以,需要 ``。 + +--- + +## 题目二:(待橙子补充) + +> 建议覆盖:`max/min`、`swap` 等 `` 函数 + +--- + +### 核心代码(一句话) +> + +### 自然语言思路 + + +### 伪代码 + + +### 真实代码 +```cpp + +``` + +### 注意点 + + +--- + +## 题目三:(待橙子补充) + +> 建议覆盖:`isalpha`、`isdigit`、`islower`、`isupper`、`tolower`、`toupper` 等 `` 函数 + +--- + +### 核心代码(一句话) +> + +### 自然语言思路 + + +### 伪代码 + + +### 真实代码 +```cpp + +``` + +### 注意点 + + +--- + +## 本课打油诗 + +> 自带函数真方便,头文件里全找见。 +> 省心省力少出错,一行代码就实现。 + +--- + +## 课后作业 +(待补充) +1. +2. diff --git a/第01课-C++自带函数/编程题-教师版.md b/第01课-C++自带函数/编程题-教师版.md new file mode 100644 index 0000000..1c84f0d --- /dev/null +++ b/第01课-C++自带函数/编程题-教师版.md @@ -0,0 +1,632 @@ +# 第1课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | GESP等级 | 对应课堂题 | +|---|---|---| +| `sqrt`、`pow`、`abs`及 `` | 一级巩固/二级拓展 | 课堂1 | +| `max`、`min`、`swap`及 `` | 二级 | 课堂2 | +| `max`、`min` 综合应用 | 二级 | 课堂3 | +| `isalpha`、`isdigit`、`islower`、`isupper`、`tolower`、`toupper` | 一级/二级 | 课堂4 | + +> 难度均为本课内部的相对难度。参考程序采用 C++17。 + +## 一、课堂练习(4题) + +### 课堂1:三点中最近的一对【适中】 + +**题目描述:** 输入平面上三个点的坐标,求三对点之间的最短距离,结果保留两位小数。要求使用 `sqrt`、`pow` 和 `min`。 + +**输入格式:** 输入六个实数,依次表示三个点的横坐标和纵坐标。 + +**输出格式:** 输出最短距离,保留两位小数。 + +**数据范围:** 每个坐标的绝对值不超过 1000。 + +**输入样例:** +```text +0 0 3 4 0 2 +``` + +**输出样例:** +```text +2.00 +``` + +**解题思路:** 用勾股定理分别求出三段距离,再用 `min` 取最小值。 + +**参考代码:** +```cpp +#include +#include +#include +#include +using namespace std; + +double distance(double x1, double y1, double x2, double y2) { + return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2)); +} + +int main() { + double x1, y1, x2, y2, x3, y3; + cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3; + double d1 = distance(x1, y1, x2, y2); + double d2 = distance(x1, y1, x3, y3); + double d3 = distance(x2, y2, x3, y3); + cout << fixed << setprecision(2) << min(d1, min(d2, d3)); + return 0; +} +``` + +**易错提醒:** `sqrt` 返回浮点数;格式化输出需要包含 ``。 + +### 课堂2:整理三个数【适中】 + +**题目描述:** 输入三个整数,第一行输出最大值和最小值;再使用 `swap` 将三个变量调整为升序,第二行输出调整结果。 + +**输入格式:** 输入三个整数。 + +**输出格式:** 第一行输出最大值和最小值;第二行输出升序结果。 + +**数据范围:** `-10^9≤a,b,c≤10^9`。 + +**输入样例:** +```text +8 2 5 +``` + +**输出样例:** +```text +8 2 +2 5 8 +``` + +**解题思路:** 用嵌套的 `max`、`min` 求最值;再进行三次相邻比较,需要时调用 `swap`。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int a, b, c; + cin >> a >> b >> c; + cout << max(a, max(b, c)) << " " << min(a, min(b, c)) << "\n"; + if (a > b) swap(a, b); + if (b > c) swap(b, c); + if (a > b) swap(a, b); + cout << a << " " << b << " " << c; + return 0; +} +``` + +**易错提醒:** `swap` 的参数必须是两个变量,不能传入 `a+b` 这样的表达式。 + +### 课堂3:去掉最高分和最低分【适中】 + +**题目描述:** 输入若干个评委分数,去掉一个最高分和一个最低分,输出剩余分数之和。不得使用排序。 + +**输入格式:** 第一行输入评委人数;第二行输入所有评委给出的分数。 + +**输出格式:** 输出剩余分数之和。 + +**数据范围:** `3≤n≤100`,分数为 `0~100`。 + +**输入样例:** +```text +5 +9 7 10 8 6 +``` + +**输出样例:** +```text +24 +``` + +**解题思路:** 遍历时同时计算总分、最高分和最低分,最后用总分减去两个极值。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n, score, sum = 0, biggest, smallest; + cin >> n; + cin >> score; + sum = biggest = smallest = score; + for (int i = 1; i < n; i++) { + cin >> score; + sum += score; + biggest = max(biggest, score); + smallest = min(smallest, score); + } + cout << sum - biggest - smallest; + return 0; +} +``` + +**易错提醒:** 最高分和最低分要用第一个合法分数初始化;即使最高分重复,也只去掉一个。 + +### 课堂4:字符身份证【适中】 + +**题目描述:** 输入一个可见字符。若为字母,输出 `LETTER` 和相反大小写的字符;若为数字,输出 `DIGIT`;否则输出 `OTHER`。 + +**输入格式:** 一个可见字符。 + +**输出格式:** 按题目要求输出。 + +**数据范围:** 标准 ASCII 可见字符。 + +**输入样例:** +```text +G +``` + +**输出样例:** +```text +LETTER g +``` + +**解题思路:** 用 `isalpha`、`isdigit` 分类,用 `islower` 或 `isupper` 判断大小写,再调用 `toupper` 或 `tolower` 转换。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + char c; + cin >> c; + if (isalpha(c)) { + cout << "LETTER "; + if (islower(c)) cout << (char)toupper(c); + else if (isupper(c)) cout << (char)tolower(c); + } else if (isdigit(c)) { + cout << "DIGIT"; + } else { + cout << "OTHER"; + } + return 0; +} +``` + +**易错提醒:** `tolower` 和 `toupper` 返回的是字符编码,输出字符时应转换为 `char`。 + +## 二、课后作业(3题) + +### 课后1:离原点的距离【简单】 + +**题目描述:** 输入点 `(x,y)`,输出它到原点的距离,保留两位小数。 + +**输入格式:** 输入两个实数,依次表示横坐标和纵坐标。 + +**输出格式:** 距离,保留两位小数。 + +**数据范围:** 坐标绝对值不超过 1000。 + +**输入样例:** +```text +3 4 +``` + +**输出样例:** +```text +5.00 +``` + +**解题思路:** 根据勾股定理计算 `sqrt(pow(x,2)+pow(y,2))`。 + +**参考代码:** +```cpp +#include +#include +#include +using namespace std; + +int main() { + double x, y; + cin >> x >> y; + cout << fixed << setprecision(2) + << sqrt(pow(x, 2) + pow(y, 2)); + return 0; +} +``` + +**易错提醒:** 不要忘记 `` 和 ``。 + +### 课后2:两数差的绝对值【简单】 + +**题目描述:** 输入两个整数,输出它们差的绝对值。 + +**输入格式:** 输入两个整数。 + +**输出格式:** 输出 `|a-b|`。 + +**数据范围:** 两数绝对值不超过 10000。 + +**输入样例:** +```text +3 9 +``` + +**输出样例:** +```text +6 +``` + +**解题思路:** 直接调用 `abs(a-b)`。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int a, b; + cin >> a >> b; + cout << abs(a - b); + return 0; +} +``` + +**易错提醒:** 先求差再取绝对值,不要误写成 `abs(a)-abs(b)`。 + +### 课后3:数字还是字母【简单】 + +**题目描述:** 输入一个字符。是数字输出 `1`,是字母输出 `2`,其他字符输出 `0`。 + +**输入格式:** 一个字符。 + +**输出格式:** 一个整数。 + +**数据范围:** 标准 ASCII 可见字符。 + +**输入样例:** +```text +8 +``` + +**输出样例:** +```text +1 +``` + +**解题思路:** 依次调用 `isdigit` 和 `isalpha`。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + char c; + cin >> c; + if (isdigit(c)) cout << 1; + else if (isalpha(c)) cout << 2; + else cout << 0; + return 0; +} +``` + +**易错提醒:** 字符数字 `'8'` 与整数 `8` 不同。 + +## 三、扩展练习(7题) + +### 扩展1:正方形对角线【基础综合】 + +**题目描述:** 输入正方形边长,输出对角线长度,保留三位小数。 + +**输入格式:** 输入一个正实数,表示正方形的边长。 + +**输出格式:** 对角线长度。 + +**数据范围:** `0 +#include +#include +using namespace std; + +int main() { + double a; + cin >> a; + cout << fixed << setprecision(3) << sqrt(2.0) * a; + return 0; +} +``` + +**易错提醒:** 使用 `2.0` 可以明确进行浮点运算。 + +### 扩展2:三个数的中间数【基础综合】 + +**题目描述:** 输入三个互不相同的整数,使用 `max` 和 `min` 输出大小居中的数。 + +**输入格式:** 三个整数。 + +**输出格式:** 中间数。 + +**数据范围:** 每个数的绝对值不超过 `10^9`。 + +**输入样例:** +```text +9 2 6 +``` + +**输出样例:** +```text +6 +``` + +**解题思路:** 三数之和减去最大值和最小值。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + long long a, b, c; + cin >> a >> b >> c; + long long biggest = max(a, max(b, c)); + long long smallest = min(a, min(b, c)); + cout << a + b + c - biggest - smallest; + return 0; +} +``` + +**易错提醒:** 三数之和可能超过 `int`,这里使用 `long long`。 + +### 扩展3:成绩极差【中等】 + +**题目描述:** 输入若干个成绩,输出最低分、最高分以及两者之差。 + +**输入格式:** 第一行输入成绩数量;第二行输入所有成绩。 + +**输出格式:** 依次输出最低分、最高分和极差。 + +**数据范围:** `1≤n≤100`,成绩为 `0~100`。 + +**输入样例:** +```text +4 +80 60 90 70 +``` + +**输出样例:** +```text +60 90 30 +``` + +**解题思路:** 用第一个成绩初始化最值,随后逐一调用 `max` 和 `min` 更新。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n, score, biggest, smallest; + cin >> n; + cin >> score; + biggest = smallest = score; + for (int i = 1; i < n; i++) { + cin >> score; + biggest = max(biggest, score); + smallest = min(smallest, score); + } + cout << smallest << " " << biggest << " " << biggest - smallest; + return 0; +} +``` + +**易错提醒:** 当只有一个成绩时,最低分和最高分相同,极差为 0。 + +### 扩展4:口令规范化【中等】 + +**题目描述:** 输入一个不含空格的口令,把其中所有字母转成大写,数字和符号保持不变。 + +**输入格式:** 一个不含空格的字符串。 + +**输出格式:** 转换后的字符串。 + +**数据范围:** 字符串长度不超过 100。 + +**输入样例:** +```text +GesP2026! +``` + +**输出样例:** +```text +GESP2026! +``` + +**解题思路:** 逐个检查字符;是字母就调用 `toupper`。 + +**参考代码:** +```cpp +#include +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + for (int i = 0; i < (int)s.size(); i++) { + if (isalpha(s[i])) s[i] = (char)toupper(s[i]); + } + cout << s; + return 0; +} +``` + +**易错提醒:** 不要改动数字和符号。 + +### 扩展5:离目标最近的数【中等】 + +**题目描述:** 输入 `n` 个整数和目标值 `t`,输出与 `t` 的差的绝对值最小的数;若并列,输出较小者。 + +**输入格式:** 第一行输入整数数量和目标值;第二行输入所有整数。 + +**输出格式:** 满足条件的整数。 + +**数据范围:** `1≤n≤100`,输入整数绝对值不超过 10000。 + +**输入样例:** +```text +4 10 +8 12 15 3 +``` + +**输出样例:** +```text +8 +``` + +**解题思路:** 用第一个数初始化答案;比较 `abs(x-t)`,相等时比较数值大小。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n, t, x, answer; + cin >> n >> t >> answer; + for (int i = 1; i < n; i++) { + cin >> x; + if (abs(x - t) < abs(answer - t) || + (abs(x - t) == abs(answer - t) && x < answer)) { + answer = x; + } + } + cout << answer; + return 0; +} +``` + +**易错提醒:** 不能把答案初始化为 0,因为 0 不一定在输入中。 + +### 扩展6:最接近平均值的成绩【略难】 + +**题目描述:** 输入 `n` 个整数成绩,求平均值,并输出最接近平均值的成绩;若并列,输出较小者。 + +**输入格式:** 第一行输入成绩数量;第二行输入所有成绩。 + +**输出格式:** 一个成绩。 + +**数据范围:** `1≤n≤100`,成绩为 `0~100`。 + +**输入样例:** +```text +4 +60 70 80 100 +``` + +**输出样例:** +```text +80 +``` + +**解题思路:** 先求浮点平均值,再使用 `abs` 进行二次打擂台。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n, a[105], sum = 0; + cin >> n; + for (int i = 0; i < n; i++) { + cin >> a[i]; + sum += a[i]; + } + double average = 1.0 * sum / n; + int answer = a[0]; + for (int i = 1; i < n; i++) { + double now = abs(a[i] - average); + double best = abs(answer - average); + if (now < best || (now == best && a[i] < answer)) { + answer = a[i]; + } + } + cout << answer; + return 0; +} +``` + +**易错提醒:** 平均值必须使用浮点除法;并列规则不能遗漏。 + +### 扩展7:统一大小写并分类统计【略难】 + +**题目描述:** 输入一个不含空格的字符串,把所有字母转成小写,并统计原字符串中大写字母、小写字母和数字的数量。 + +**输入格式:** 输入一个不含空格的字符串。 + +**输出格式:** 第一行输出转换后的字符串;第二行依次输出三类字符数量。 + +**数据范围:** 字符串长度为 1~100。 + +**输入样例:** +```text +AbC2026xY +``` + +**输出样例:** +```text +abc2026xy +3 3 4 +``` + +**解题思路:** 遍历原字符串,先分类计数,再对字母调用 `tolower`。 + +**参考代码:** +```cpp +#include +#include +#include +using namespace std; + +int main() { + string s; + int upper = 0, lower = 0, digit = 0; + cin >> s; + for (char &c : s) { + if (isupper(c)) upper++; + else if (islower(c)) lower++; + else if (isdigit(c)) digit++; + if (isalpha(c)) c = (char)tolower(c); + } + cout << s << "\n"; + cout << upper << " " << lower << " " << digit; + return 0; +} +``` + +**易错提醒:** 分类统计必须依据转换前的字符;数字不参与大小写转换。 diff --git a/第02课-循环结构/教案-学生版.md b/第02课-循环结构/教案-学生版.md new file mode 100644 index 0000000..284f356 --- /dev/null +++ b/第02课-循环结构/教案-学生版.md @@ -0,0 +1,160 @@ +# 第2课:循环结构 + 数位拆解 + +## 教学目标 +- 掌握 for、while 和 do-while 循环的使用场景 +- 掌握数位拆解技巧(取个位、去个位) +- 能用循环判断回文数 +- 了解水仙花数的判断方法 + +## 核心考点:循环边界与数位拆解 + +### 考什么 +判断循环次数和结束条件,并用循环完成求各位和、________数字、判断回文数等任务。 + +### 必须理解 +次数明确时常用________;结束条件更自然时常用________。数位拆解每轮依次完成:________、使用这一位、________。如果后面还要使用原数,必须先________。 + +### 核心写法 +```cpp +int num = ________; +int rev = 0; +while (num > 0) { + int d = num ________ 10; + rev = rev * ________ + d; + num ________ 10; +} +``` + +### `do-while` 循环 + +`do-while` 的特点:循环体至少执行 ______ 次。 + +```cpp +do { + // 循环体 +} while (条件); // 结尾不要漏掉 ______ +``` + +### 常见错误 +- 忘记更新循环变量会发生什么?________________ +- `i < n` 与 `i <= n` 的执行次数有什么区别?____________ +- 为什么反转前要备份原数?________________ + +### 判断是否掌握 +- [ ] 我能判断循环的起点、终点和次数。 +- [ ] 我能写出取个位与去个位。 +- [ ] 我能独立完成数字反转。 + +--- + +## 课堂编程题 + +### 课堂1:奇偶分组统计【适中】 + +**题目描述:** 输入正整数 `n`,分别求 `1~n` 中奇数之和、偶数之和,并计算 `1~n` 的乘积。若乘积超过 `10^18`,输出 `TOO LARGE`。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 第一行奇数和与偶数和;第二行乘积或 `TOO LARGE`。 + +**数据范围:** `1≤n≤30`。 + +**输入样例:** +```text +5 +``` + +**输出样例:** +```text +9 6 +120 +``` + +**解题思路:** 一个 `for` 循环同时完成奇偶累加;累乘前判断 `product>10^18/i`,避免溢出。 + +**易错提醒:** 范围是 `1~n`,循环条件必须为 `i<=n`;累加器和累乘器的初值分别为 0 和 1。 + +--- + +### 课堂2:数字分析报告【适中】 + +**题目描述:** 输入一个非负整数,输出它的位数、各位数字之和、最大数字和反转数。 + +**输入格式:** 输入一个非负整数。 + +**输出格式:** 依次输出位数、各位和、最大数字、反转数。 + +**数据范围:** `0≤n≤10^9`。 + +**输入样例:** +```text +50230 +``` + +**输出样例:** +```text +5 10 5 3205 +``` + +**解题思路:** 备份原数,用 `while` 重复"取个位、使用个位、去个位";单独处理 `n=0`。 + +**易错提醒:** 反转后的前导零不会保留;0 本身有一位,不能被普通循环漏掉。 + +--- + +### 课堂3:寻找第一个合格数【适中】 + +**题目描述:** 从 `1~n` 中寻找第一个同时满足"能被 7 整除、不能被 5 整除、各位和大于 10"的数。跳过所有含数字 4 的数;找到后立即输出,否则输出 `NONE`。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 第一个合格数或 `NONE`。 + +**数据范围:** `1≤n≤100000`。 + +**输入样例:** +```text +100 +``` + +**输出样例:** +```text +77 +``` + +**解题思路:** 外层枚举数字;拆位检查含 4 和各位和。含 4 时 `continue`,找到答案时 `break`。 + +**易错提醒:** `continue` 跳过本轮,`break` 结束整个当前循环,两者含义不同。 + +--- + +### 课堂4:区间素数统计【适中·双层循环】 + +**题目描述:** 输入 `L` 和 `R`,输出闭区间内的所有素数及素数个数。 + +**输入格式:** 输入区间的左端点和右端点。 + +**输出格式:** 第一行按升序输出素数;第二行输出个数。没有素数时第一行输出 `NONE`。 + +**数据范围:** `1≤L≤R≤10000`。 + +**输入样例:** +```text +10 20 +``` + +**输出样例:** +```text +11 13 17 19 +4 +``` + +**解题思路:** 外层枚举候选数,内层从 2 检查到平方根;找到因数立即 `break`。 + +**易错提醒:** 1 不是素数;内层的 `break` 只结束因数检查,不会结束外层枚举。 + +--- + +## 课后作业 + +> 请完成本课对应的课后作业,见编程题文件。 diff --git a/第02课-循环结构/教案-教师版.md b/第02课-循环结构/教案-教师版.md new file mode 100644 index 0000000..e7b8869 --- /dev/null +++ b/第02课-循环结构/教案-教师版.md @@ -0,0 +1,198 @@ +# 第2课:循环结构 + 数位拆解 + +## 教学目标 +- 掌握 for、while 和 do-while 循环的使用场景 +- 掌握数位拆解技巧(取个位、去个位) +- 能用循环判断回文数 +- 了解水仙花数的判断方法 + +## 核心考点:循环边界与数位拆解 + +### 考什么 +常见题型要求重复执行固定次数、在条件成立时持续循环,或逐位处理一个整数,例如求各位和、反转数字、判断回文数。考点既包括循环写法,也包括循环何时开始、何时结束。 + +### 必须理解 +`for` 适合次数明确的重复,`while` 适合结束条件更自然的过程。数位拆解每轮做三件事:`n % 10` 取个位,使用这个个位完成统计或拼接,`n /= 10` 去掉个位。因为 n 会不断变小,若后面还要与原数比较,必须提前备份。循环边界要保证每个需要处理的值恰好进入一次,不能漏掉终点或多执行一轮。 + +### 核心写法 +```cpp +int original = n; +int reversed = 0; +while (n > 0) { + int digit = n % 10; + reversed = reversed * 10 + digit; + n /= 10; +} +bool isPalindrome = (reversed == original); +``` +取位、使用、去位的顺序不能乱;`n /= 10` 还保证循环最终结束。 + +### `do-while` 循环 + +`do-while` 会先执行循环体,再判断条件,因此循环体至少执行一次。适合“先操作一次,再决定是否继续”的场景。 + +```cpp +int number; +do { + cin >> number; +} while (number != 0); +``` + +注意结尾的分号不能漏掉。三种循环选择原则:次数明确优先 `for`;先判断再重复常用 `while`;至少执行一次使用 `do-while`。 + +### 常见错误 +- 忘记备份原数,拆解结束后 n 已变成 0。 +- 忘记更新循环变量,造成死循环。 +- 把 `i < n` 和 `i <= n` 混用,导致少算或多算一次。 +- 反转数字时漏写 `reversed * 10`,无法给新个位腾出位置。 + +### 判断是否掌握 +能判断一个任务该用 `for` 还是 `while`,能准确写出循环范围,并能独立完成取个位、去个位、求各位和或反转数字。 + +--- + +## 题目一:判断回文数 + +### 核心代码(一句话) +> 回文数的核心:**把数字反转,看反转后和原数是否相等。** 反转靠三行代码——取个位、拼新数、去个位。 + +### 自然语言思路 +回文数就是"正着读反着读都一样"的数,比如 12321、4554、7。 + +**怎么判断?分三步走:** + +**第一步:备份原数。** 因为待会儿要拆解它,拆完就没了,所以先用 `temp = n` 把原数存好。 + +**第二步:反转数字。** 这是核心!口诀:**"取个位、拼新数、去个位"**,循环直到原数变成 0。 + +以 123 为例,手推一遍: + +| 轮次 | 原数 temp | 个位 d = temp%10 | 反转数 rev = rev×10+d | temp = temp/10 | +|------|-----------|-------------------|------------------------|-----------------| +| 第1轮 | 123 | 3 | 0×10+3 = **3** | 12 | +| 第2轮 | 12 | 2 | 3×10+2 = **32** | 1 | +| 第3轮 | 1 | 1 | 32×10+1 = **321** | 0 | +| 结束 | 0 | — | **321** | — | + +反转 123 得到 321。这个过程就像把数字"倒着拼回去"。 + +**第三步:比较。** 原数 n(备份的)和反转数 rev 相等 → 回文数! + +### 伪代码 +``` +输入 n +temp = n // 备份原数 +rev = 0 // 反转数初始为0 +当 temp > 0: + d = temp % 10 // 取个位 + rev = rev * 10 + d // 拼到反转数的末尾 + temp = temp / 10 // 去掉个位 +如果 rev == n: + 输出 "是回文数" +否则: + 输出 "不是回文数" +``` + +### 真实代码 +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + int rev = 0; // 存放反转后的数 + int temp = n; // 备份原数,因为要拆解 + + while (temp > 0) { + int d = temp % 10; // ① 取个位 + rev = rev * 10 + d; // ② 拼到新数末尾 + temp /= 10; // ③ 去掉个位 + } + + if (rev == n) { + cout << "是回文数" << endl; + } else { + cout << "不是回文数" << endl; + } + return 0; +} +``` + +### 注意点 +1. **一定要备份原数**:`temp = n` 这一步不能省!否则 n 被拆解成 0 了,最后没法比较。 +2. **`while` vs `for`**:数位拆解用 `while` 更自然,因为事先不知道数字有几位。`for` 适用于知道循环次数的情况。 +3. **`temp % 10` 取个位**:任何整数模 10 就得到个位。如 12345 % 10 = 5。 +4. **`temp / 10` 去个位**:整数除法自动舍去余数。如 12345 / 10 = 1234。 +5. **边界:0 和负数**:0 是回文数(0反转还是0)。负数一般不算回文数(-121 反转是 121-),GESP 一级不会考负数回文。 +6. **循环终止条件**:`temp > 0`,不是 `temp >= 0`(会导致死循环,因为 0/10 还是 0)。 + +--- + +## 题目二:(待橙子补充) + +> 建议覆盖:水仙花数、for循环基本用法等 + +--- + +### 核心代码(一句话) +> + +### 自然语言思路 + + +### 伪代码 + + +### 真实代码 +```cpp + +``` + +### 注意点 + + +--- + +## 题目三:(待橙子补充) + +> 建议覆盖:break/continue、循环综合练习等 + +--- + +### 核心代码(一句话) +> + +### 自然语言思路 + + +### 伪代码 + + +### 真实代码 +```cpp + +``` + +### 注意点 + + +--- + +## 本课打油诗 + +> **(数位拆解)** +> 循环取位倒着排,个位拼到新数来。 +> 除十去尾继续转,正反相等回文哉。 + +> **(回文数判断)** +> 反转数字比原数,相等就是回文数。 +> 备份原数不能忘,拆完再比才无误。 + +--- + +## 课后作业 +(待补充) +1. +2. diff --git a/第02课-循环结构/编程题-教师版.md b/第02课-循环结构/编程题-教师版.md new file mode 100644 index 0000000..70e91f6 --- /dev/null +++ b/第02课-循环结构/编程题-教师版.md @@ -0,0 +1,690 @@ +# 第5课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | GESP等级 | 对应课堂题 | +|---|---|---| +| `for`、累加、累乘与循环边界 | 一级必考 | 课堂1 | +| `while`、取个位、去个位、数字反转 | 一级/二级 | 课堂2 | +| `break`、`continue` 与循环控制 | 一级必考 | 课堂3 | +| 双层循环、外层枚举对象、内层检查条件 | 一级巩固/二级 | 课堂4 | + +> 本课包含 6 道双层循环题,作为第 6 课嵌套循环的过渡。 + +## 一、课堂练习(4题) + +### 课堂1:奇偶分组统计【适中】 + +**题目描述:** 输入正整数 `n`,分别求 `1~n` 中奇数之和、偶数之和,并计算 `1~n` 的乘积。若乘积超过 `10^18`,输出 `TOO LARGE`。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 第一行奇数和与偶数和;第二行乘积或 `TOO LARGE`。 + +**数据范围:** `1≤n≤30`。 + +**输入样例:** +```text +5 +``` + +**输出样例:** +```text +9 6 +120 +``` + +**解题思路:** 一个 `for` 循环同时完成奇偶累加;累乘前判断 `product>10^18/i`,避免溢出。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + long long oddSum = 0, evenSum = 0, product = 1; + bool tooLarge = false; + cin >> n; + for (int i = 1; i <= n; i++) { + if (i % 2 == 0) evenSum += i; + else oddSum += i; + if (!tooLarge) { + if (product > 1000000000000000000LL / i) tooLarge = true; + else product *= i; + } + } + cout << oddSum << " " << evenSum << "\n"; + if (tooLarge) cout << "TOO LARGE"; + else cout << product; + return 0; +} +``` + +**易错提醒:** 范围是 `1~n`,循环条件必须为 `i<=n`;累加器和累乘器的初值分别为 0 和 1。 + +### 课堂2:数字分析报告【适中】 + +**题目描述:** 输入一个非负整数,输出它的位数、各位数字之和、最大数字和反转数。 + +**输入格式:** 输入一个非负整数。 + +**输出格式:** 依次输出位数、各位和、最大数字、反转数。 + +**数据范围:** `0≤n≤10^9`。 + +**输入样例:** +```text +50230 +``` + +**输出样例:** +```text +5 10 5 3205 +``` + +**解题思路:** 备份原数,用 `while` 重复“取个位、使用个位、去个位”;单独处理 `n=0`。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n; + cin >> n; + if (n == 0) { + cout << "1 0 0 0"; + return 0; + } + int temp = n, count = 0, sum = 0, biggest = 0, reversed = 0; + while (temp > 0) { + int digit = temp % 10; + count++; + sum += digit; + biggest = max(biggest, digit); + reversed = reversed * 10 + digit; + temp /= 10; + } + cout << count << " " << sum << " " << biggest << " " << reversed; + return 0; +} +``` + +**易错提醒:** 反转后的前导零不会保留;0 本身有一位,不能被普通循环漏掉。 + +### 课堂3:寻找第一个合格数【适中】 + +**题目描述:** 从 `1~n` 中寻找第一个同时满足“能被 7 整除、不能被 5 整除、各位和大于 10”的数。跳过所有含数字 4 的数;找到后立即输出,否则输出 `NONE`。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 第一个合格数或 `NONE`。 + +**数据范围:** `1≤n≤100000`。 + +**输入样例:** +```text +100 +``` + +**输出样例:** +```text +77 +``` + +**解题思路:** 外层枚举数字;拆位检查含 4 和各位和。含 4 时 `continue`,找到答案时 `break`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, answer = -1; + cin >> n; + for (int x = 1; x <= n; x++) { + int temp = x, sum = 0; + bool hasFour = false; + while (temp > 0) { + int digit = temp % 10; + if (digit == 4) hasFour = true; + sum += digit; + temp /= 10; + } + if (hasFour) continue; + if (x % 7 == 0 && x % 5 != 0 && sum > 10) { + answer = x; + break; + } + } + if (answer == -1) cout << "NONE"; + else cout << answer; + return 0; +} +``` + +**易错提醒:** `continue` 跳过本轮,`break` 结束整个当前循环,两者含义不同。 + +### 课堂4:区间素数统计【适中·双层循环】 + +**题目描述:** 输入 `L` 和 `R`,输出闭区间内的所有素数及素数个数。 + +**输入格式:** 输入区间的左端点和右端点。 + +**输出格式:** 第一行按升序输出素数;第二行输出个数。没有素数时第一行输出 `NONE`。 + +**数据范围:** `1≤L≤R≤10000`。 + +**输入样例:** +```text +10 20 +``` + +**输出样例:** +```text +11 13 17 19 +4 +``` + +**解题思路:** 外层枚举候选数,内层从 2 检查到平方根;找到因数立即 `break`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int L, R, count = 0; + cin >> L >> R; + for (int x = L; x <= R; x++) { + if (x < 2) continue; + bool prime = true; + for (int d = 2; d * d <= x; d++) { + if (x % d == 0) { + prime = false; + break; + } + } + if (prime) { + if (count) cout << " "; + cout << x; + count++; + } + } + if (count == 0) cout << "NONE"; + cout << "\n" << count; + return 0; +} +``` + +**易错提醒:** 1 不是素数;内层的 `break` 只结束因数检查,不会结束外层枚举。 + +## 二、课后作业(3题) + +### 课后1:从 1 加到 n【简单】 + +**题目描述:** 输入 `n`,用循环求 `1+2+…+n`。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 累加和。 + +**数据范围:** `1≤n≤10^6`。 + +**输入样例:** +```text +5 +``` + +**输出样例:** +```text +15 +``` + +**解题思路:** 累加器从 0 开始,循环范围为 `1~n`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + long long sum = 0; + cin >> n; + for (int i = 1; i <= n; i++) sum += i; + cout << sum; + return 0; +} +``` + +**易错提醒:** 和可能超过 `int`,使用 `long long`。 + +### 课后2:各位数字之和【简单】 + +**题目描述:** 输入一个正整数,输出各位数字之和。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 各位数字之和。 + +**数据范围:** `1≤n≤10^9`。 + +**输入样例:** +```text +5082 +``` + +**输出样例:** +```text +15 +``` + +**解题思路:** 每轮用 `n%10` 取个位并累加,再用 `n/=10` 去个位。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, sum = 0; + cin >> n; + while (n > 0) { + sum += n % 10; + n /= 10; + } + cout << sum; + return 0; +} +``` + +**易错提醒:** 循环内必须更新 `n`,否则会形成死循环。 + +### 课后3:跳过 3 的倍数【简单】 + +**题目描述:** 输出 `1~n` 中所有不是 3 的倍数的数。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 符合条件的数,以空格分隔。 + +**数据范围:** `1≤n≤100`。 + +**输入样例:** +```text +7 +``` + +**输出样例:** +```text +1 2 4 5 7 +``` + +**解题思路:** 遇到 3 的倍数时使用 `continue`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + bool first = true; + cin >> n; + for (int i = 1; i <= n; i++) { + if (i % 3 == 0) continue; + if (!first) cout << " "; + cout << i; + first = false; + } + return 0; +} +``` + +**易错提醒:** 输出空格时用 `first` 控制,可避免行末多余空格。 + +## 三、扩展练习(7题) + +### 扩展1:回文数判断【基础综合】 + +**题目描述:** 输入一个非负整数,判断是否为回文数。 + +**输入格式:** 输入一个非负整数。 + +**输出格式:** 是回文数输出 `YES`,否则输出 `NO`。 + +**数据范围:** `0≤n≤10^9`。 + +**输入样例:** +```text +12321 +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 备份原数,构造反转数并比较。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, temp, reversed = 0; + cin >> n; + temp = n; + while (temp > 0) { + reversed = reversed * 10 + temp % 10; + temp /= 10; + } + cout << (reversed == n ? "YES" : "NO"); + return 0; +} +``` + +**易错提醒:** 0 的反转仍是 0,因此程序也能正确判断。 + +### 扩展2:三位水仙花数【中等·双层思维】 + +**题目描述:** 输出 `100~n` 中所有水仙花数。三位数各位数字立方和等于原数时称为水仙花数。 + +**输入格式:** 输入一个整数,表示枚举范围的上界。 + +**输出格式:** 所有水仙花数,以空格分隔;没有则输出 `NONE`。 + +**数据范围:** `100≤n≤999`。 + +**输入样例:** +```text +400 +``` + +**输出样例:** +```text +153 370 371 +``` + +**解题思路:** 外层枚举三位数,内层 `while` 拆出三位并累加立方。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count = 0; + cin >> n; + for (int x = 100; x <= n; x++) { + int temp = x, sum = 0; + while (temp > 0) { + int d = temp % 10; + sum += d * d * d; + temp /= 10; + } + if (sum == x) { + if (count) cout << " "; + cout << x; + count++; + } + } + if (count == 0) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 判断时要与原数 `x` 比较,不能与已变成 0 的 `temp` 比较。 + +### 扩展3:因数清单【中等·双层循环】 + +**题目描述:** 对 `1~n` 中的每个数,输出该数及它的所有正因数,每个数占一行。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 第 `x` 行先输出 `x:`,再输出 `x` 的所有正因数。 + +**数据范围:** `1≤n≤30`。 + +**输入样例:** +```text +3 +``` + +**输出样例:** +```text +1: 1 +2: 1 2 +3: 1 3 +``` + +**解题思路:** 外层枚举数字 `x`,内层枚举可能的因数 `d`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int x = 1; x <= n; x++) { + cout << x << ":"; + for (int d = 1; d <= x; d++) { + if (x % d == 0) cout << " " << d; + } + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 内层上界随当前数字 `x` 变化,不是固定使用 `n`。 + +### 扩展4:完数统计【中等·双层循环】 + +**题目描述:** 一个正整数的所有真因数(不含自身)之和等于它本身,则称为完数。输出 `2~n` 中所有完数。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 所有完数;没有则输出 `NONE`。 + +**数据范围:** `2≤n≤10000`。 + +**输入样例:** +```text +30 +``` + +**输出样例:** +```text +6 28 +``` + +**解题思路:** 外层枚举候选数,内层枚举 `1~x/2` 并累加因数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count = 0; + cin >> n; + for (int x = 2; x <= n; x++) { + int sum = 0; + for (int d = 1; d <= x / 2; d++) { + if (x % d == 0) sum += d; + } + if (sum == x) { + if (count) cout << " "; + cout << x; + count++; + } + } + if (count == 0) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 真因数不包含自身;每次检查新数字时 `sum` 都要清零。 + +### 扩展5:乘积等于目标的数对【中等·双层循环】 + +**题目描述:** 输入 `n` 和目标 `m`,输出所有满足 `1≤a +using namespace std; + +int main() { + int n, m, count = 0; + cin >> n >> m; + for (int a = 1; a <= n; a++) { + for (int b = a + 1; b <= n; b++) { + if (a * b == m) { + cout << a << " " << b << "\n"; + count++; + } + } + } + if (count == 0) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** `b` 从 `a+1` 开始,才能保证每个无序数对只输出一次。 + +### 扩展6:回文素数【略难·双层循环】 + +**题目描述:** 输出 `L~R` 中既是回文数又是素数的数。 + +**输入格式:** 输入区间的左端点和右端点。 + +**输出格式:** 所有回文素数;没有则输出 `NONE`。 + +**数据范围:** `1≤L≤R≤100000`。 + +**输入样例:** +```text +10 150 +``` + +**输出样例:** +```text +11 101 131 +``` + +**解题思路:** 外层枚举;先反转判断回文,通过后再用内层循环判断素数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int L, R, count = 0; + cin >> L >> R; + for (int x = L; x <= R; x++) { + int temp = x, reversed = 0; + while (temp > 0) { + reversed = reversed * 10 + temp % 10; + temp /= 10; + } + if (reversed != x || x < 2) continue; + + bool prime = true; + for (int d = 2; d * d <= x; d++) { + if (x % d == 0) { + prime = false; + break; + } + } + if (prime) { + if (count) cout << " "; + cout << x; + count++; + } + } + if (count == 0) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 先判断回文可减少素数检查次数;1 仍然不是素数。 + +### 扩展7:最接近目标的因数积【略难·双层循环】 + +**题目描述:** 输入 `n` 和目标 `target`。在所有满足 `1≤a≤b≤n` 的数对中,寻找乘积与目标差的绝对值最小的一对;并列时先选较小的 `a`,再选较小的 `b`。 + +**输入格式:** 输入数对中每个数的上界和目标值。 + +**输出格式:** 输出 `a b a*b`。 + +**数据范围:** `1≤n≤500`,`1≤target≤250000`。 + +**输入样例:** +```text +10 53 +``` + +**输出样例:** +```text +6 9 54 +``` + +**解题思路:** 双层枚举所有合法数对,用二次打擂台比较乘积距离、`a`、`b`。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n, target; + cin >> n >> target; + int bestA = 1, bestB = 1; + for (int a = 1; a <= n; a++) { + for (int b = a; b <= n; b++) { + int now = a * b; + int best = bestA * bestB; + if (abs(now - target) < abs(best - target) || + (abs(now - target) == abs(best - target) && a < bestA) || + (abs(now - target) == abs(best - target) && + a == bestA && b < bestB)) { + bestA = a; + bestB = b; + } + } + } + cout << bestA << " " << bestB << " " << bestA * bestB; + return 0; +} +``` + +**易错提醒:** 枚举范围允许 `a=b`;并列规则必须按顺序完整处理。 diff --git a/第03课-自定义函数/教案-学生版.md b/第03课-自定义函数/教案-学生版.md new file mode 100644 index 0000000..bed95a6 --- /dev/null +++ b/第03课-自定义函数/教案-学生版.md @@ -0,0 +1,151 @@ +# 第3课:自定义函数(教学拓展) + +> 等级说明:本课用于拓展代码分解能力,不属于 GESP 一级、二级必考内容。 + +## 教学目标 +- 掌握函数的定义、调用与声明 +- 理解形参与实参、返回值、void函数 +- 理解函数重载(同名不同参) +- 理解局部变量与全局变量的作用域 + +## 核心考点:函数的参数、返回值与调用过程 + +### 考什么 +补全函数定义,区分________与________,并根据函数调用过程判断输出。 + +### 必须理解 +定义函数时括号里的变量叫________;调用时传入的值叫________。执行到 `return` 时,函数会________________。普通值传递中,形参得到的是实参的________。 + +### 核心写法 +```cpp +int maxValue(int a, int b) { + if (a > b) return ________; + return ________; +} + +int ans = maxValue(________, ________); +``` + +### 常见错误 +- 返回类型不是 `void`,却忘记:________________ +- 修改普通形参是否一定改变外部实参?________________ +- 局部变量可以在哪个范围使用?________________ + +### 判断是否掌握 +- [ ] 我能区分形参和实参。 +- [ ] 我能说清函数调用与返回的执行顺序。 +- [ ] 我能独立编写有参数、有返回值的函数。 + +--- + +## 课堂编程题 + +### 课堂1:三个数的最大公约数【适中】 + +**题目描述:** 编写函数 `gcd(a,b)`,使用辗转相除法求两个正整数的最大公约数。在 `main` 前写函数声明、`main` 后写函数定义,连续调用两次求三个数的最大公约数。 + +**输入格式:** 输入三个正整数。 + +**输出格式:** 三个数的最大公约数。 + +**数据范围:** `1≤a,b,c≤10^9`。 + +**输入样例:** +```text +12 18 30 +``` + +**输出样例:** +```text +6 +``` + +**解题思路:** 先求 `gcd(a,b)`,再把结果与 `c` 求最大公约数,体会函数声明、定义和多次调用。 + +**易错提醒:** 函数声明必须与定义的返回类型、函数名和参数列表一致。 + +--- + +### 课堂2:统计区间内的素数【适中】 + +**题目描述:** 编写有参数、有返回值的函数 `isPrime(n)`。输入 `L` 和 `R`,输出闭区间 `[L,R]` 中素数的个数。 + +**输入格式:** 输入区间的左端点和右端点。 + +**输出格式:** 素数个数。 + +**数据范围:** `1≤L≤R≤10000`。 + +**输入样例:** +```text +10 20 +``` + +**输出样例:** +```text +4 +``` + +**解题思路:** 每个实参传入函数后复制给形参 `n`;找到因数时提前返回 `false`,全部检查后返回 `true`。 + +**易错提醒:** 1 不是素数;普通值传递不会修改主函数中的实参。 + +--- + +### 课堂3:统一格式输出【适中】 + +**题目描述:** 重载函数 `printLine`:一个版本接收一个整数并输出由该整数重复 5 次组成的一行;另一个版本接收一个字符和次数,输出指定长度的一行。两个函数都没有返回值。 + +**输入格式:** 依次输入一个整数、一个字符和字符的重复次数。 + +**输出格式:** 第一行输出 5 个 `x`;第二行输出 `n` 个字符 `c`。 + +**数据范围:** `0≤x≤9`,`1≤n≤50`。 + +**输入样例:** +```text +7 * 4 +``` + +**输出样例:** +```text +77777 +**** +``` + +**解题思路:** 两个函数同名但参数列表不同,构成函数重载;只负责输出,因此返回类型为 `void`。 + +**易错提醒:** 重载依据参数数量或类型区分,不能只靠返回类型区分。 + +--- + +### 课堂4:全班累计加分【适中】 + +**题目描述:** 全局变量 `total` 表示全班累计加分。编写 `addScore(x)` 修改它;函数内部再定义同名局部变量 `total`,演示局部变量优先。每次调用后输出全局累计值。 + +**输入格式:** 第一行输入加分次数;第二行输入每次的加分值。 + +**输出格式:** 输出每次加分后的全局累计值。 + +**数据范围:** `1≤n≤20`,每次加分 `1~10`。 + +**输入样例:** +```text +3 +2 5 1 +``` + +**输出样例:** +```text +2 7 8 +``` + +**解题思路:** 用 `::total` 指明全局变量;局部变量仅在函数的大括号内有效。 + +**易错提醒:** 局部变量会遮住同名全局变量;实际编程应尽量避免故意重名,本题只用于理解作用域。 + +--- + +## 课后作业 +1. +2. diff --git a/第03课-自定义函数/教案-教师版.md b/第03课-自定义函数/教案-教师版.md new file mode 100644 index 0000000..9a3ae3e --- /dev/null +++ b/第03课-自定义函数/教案-教师版.md @@ -0,0 +1,171 @@ +# 第3课:自定义函数(教学拓展) + +> 等级说明:自定义函数属于 GESP 四级知识。本课程保留这一课,是为了训练代码分解和复用能力,不作为一级、二级考试必考内容。 + +## 教学目标 +- 掌握函数的定义、调用与声明 +- 理解形参与实参、返回值、void函数 +- 理解函数重载(同名不同参) +- 理解局部变量与全局变量的作用域 + +## 核心考点:函数的参数、返回值与调用过程 + +### 考什么 +常见题型包括补全函数定义、判断形参与实参、根据调用过程求输出,以及把一段重复逻辑封装成函数。重点是看懂数据怎样进入函数、函数怎样处理、结果怎样返回主程序。 + +### 必须理解 +定义函数时括号里的变量叫形参,是函数内部接收数据的“临时名字”;调用函数时传入的值叫实参。普通值传递会把实参复制给形参,函数内部修改形参通常不会改变外面的变量。执行到 `return` 时,函数立即结束并把结果交回调用位置;`void` 函数不返回结果。局部变量只在所属的大括号内有效,不同函数中的同名局部变量互不影响。 + +### 核心写法 +```cpp +int maxValue(int a, int b) { // a、b 是形参 + if (a > b) return a; + return b; +} + +int answer = maxValue(x, y); // x、y 是实参 +``` +调用时先计算实参,再进入函数执行;遇到 `return` 后回到原调用位置,把返回值赋给 `answer`。 + +### 常见错误 +- 声明的返回类型是 `int`,却忘记写 `return`。 +- 把函数定义写进 `main` 内部;C++ 不允许在函数内部再定义普通函数。 +- 以为修改普通形参就会修改实参,混淆“值的复制”和“原变量”。 +- 在变量作用域之外使用局部变量。 + +### 判断是否掌握 +能画出“实参传入→形参接收→函数执行→return 返回→主程序继续”的过程,并能独立编写一个有参数、有返回值的判断或计算函数。 + +--- + +## 题目一:判断素数 + +### 核心代码(一句话) +> 判断素数的核心:**从 2 试到 √n,只要有一个能整除,就不是素数。** + +### 自然语言思路 +素数就是"除了1和自己,不能被任何数整除"的数。 + +比如判断 17 是不是素数: +- 试 2:17 ÷ 2 = 8 余 1,不行 +- 试 3:17 ÷ 3 = 5 余 2,不行 +- 试 4:17 ÷ 4 = 4 余 1,不行 +- 试到 √17 ≈ 4.1,所以只需试到 4 +- 全都不行 → 17 是素数! + +为什么只试到 **√n**?因为如果 n 有一个大于 √n 的因数,那一定会有一个小于 √n 的因数跟它配对。比如 36 = 6 × 6,一个因数大于 6,另一个必然小于 6。所以试到 √n 就够了。 + +**为什么要写成函数?** 判断素数的代码以后经常用到,封装成 `isPrime(n)`,以后需要时一行调用,不用重复写。 + +### 伪代码 +``` +函数 isPrime(n): + 如果 n < 2: 返回 false // 1 不是素数 + 对于 i 从 2 到 sqrt(n): + 如果 n % i == 0: // 能被整除 + 返回 false // 不是素数,直接返回 + 返回 true // 全都试过了,是素数 + +主程序: + 输入 m + 如果 isPrime(m): 输出 "YES" + 否则: 输出 "NO" +``` + +### 真实代码 +```cpp +#include +#include +using namespace std; + +// 函数定义:判断素数 +bool isPrime(int n) { + if (n < 2) return false; // 1 和负数不是素数 + for (int i = 2; i <= sqrt(n); i++) { + if (n % i == 0) return false; // 找到因数,不是素数 + } + return true; // 全部试完,是素数 +} + +int main() { + int m; + cin >> m; + if (isPrime(m)) { + cout << "YES" << endl; + } else { + cout << "NO" << endl; + } + return 0; +} +``` + +### 注意点 +1. **1 不是素数**:必须在函数开头处理 `n < 2` 的情况,这是 GESP 高频易错点。 +2. **为什么 `i <= sqrt(n)` 而不是 `i < n`**:前者效率高很多。判断 100 是否是素数,试到 10 就够了,不用试到 99。 +3. **`sqrt` 返回 double**:`i <= sqrt(n)` 是 int 和 double 比较,C++ 会自动转换,没问题。但如果担心精度,可以写成 `i * i <= n`。 +4. **函数名要有意义**:`isPrime` 一看就知道是"判断素数",比 `f`、`check` 好得多。 +5. **局部变量 i**:i 只在 isPrime 函数里有效,main 里访问不到它 —— 这就是**局部变量**的作用域。 + +--- + +## 题目二:(待橙子补充) + +> 建议覆盖:函数重载、void函数等 + +--- + +### 核心代码(一句话) +> + +### 自然语言思路 + + +### 伪代码 + + +### 真实代码 +```cpp + +``` + +### 注意点 + + +--- + +## 题目三:(待橙子补充) + +> 建议覆盖:局部变量与全局变量对比 + +--- + +### 核心代码(一句话) +> + +### 自然语言思路 + + +### 伪代码 + + +### 真实代码 +```cpp + +``` + +### 注意点 + + +--- + +## 本课打油诗 + +> 函数写好存身旁,起名调用响当当。 +> 参数传进返结果,少写重复心不慌。 + +--- + +## 课后作业 +(待补充) +1. +2. diff --git a/第03课-自定义函数/编程题-教师版.md b/第03课-自定义函数/编程题-教师版.md new file mode 100644 index 0000000..aae1ccd --- /dev/null +++ b/第03课-自定义函数/编程题-教师版.md @@ -0,0 +1,687 @@ +# 第2课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | GESP等级 | 对应课堂题 | +|---|---|---| +| 函数声明、定义与调用 | 二级必考 | 课堂1 | +| 形参与实参、值传递、返回值、提前 `return` | 二级必考 | 课堂2 | +| `void` 函数与函数重载 | 二级 | 课堂3 | +| 局部变量、全局变量与作用域 | 二级必考 | 课堂4 | + +> 难度均为本课内部相对难度,参考程序采用 C++17。 + +## 一、课堂练习(4题) + +### 课堂1:三个数的最大公约数【适中】 + +**题目描述:** 编写函数 `gcd(a,b)`,使用辗转相除法求两个正整数的最大公约数。在 `main` 前写函数声明、`main` 后写函数定义,连续调用两次求三个数的最大公约数。 + +**输入格式:** 输入三个正整数。 + +**输出格式:** 三个数的最大公约数。 + +**数据范围:** `1≤a,b,c≤10^9`。 + +**输入样例:** +```text +12 18 30 +``` + +**输出样例:** +```text +6 +``` + +**解题思路:** 先求 `gcd(a,b)`,再把结果与 `c` 求最大公约数,体会函数声明、定义和多次调用。 + +**参考代码:** +```cpp +#include +using namespace std; + +int gcd(int a, int b); + +int main() { + int a, b, c; + cin >> a >> b >> c; + cout << gcd(gcd(a, b), c); + return 0; +} + +int gcd(int a, int b) { + while (b != 0) { + int r = a % b; + a = b; + b = r; + } + return a; +} +``` + +**易错提醒:** 函数声明必须与定义的返回类型、函数名和参数列表一致。 + +### 课堂2:统计区间内的素数【适中】 + +**题目描述:** 编写有参数、有返回值的函数 `isPrime(n)`。输入 `L` 和 `R`,输出闭区间 `[L,R]` 中素数的个数。 + +**输入格式:** 输入区间的左端点和右端点。 + +**输出格式:** 素数个数。 + +**数据范围:** `1≤L≤R≤10000`。 + +**输入样例:** +```text +10 20 +``` + +**输出样例:** +```text +4 +``` + +**解题思路:** 每个实参传入函数后复制给形参 `n`;找到因数时提前返回 `false`,全部检查后返回 `true`。 + +**参考代码:** +```cpp +#include +using namespace std; + +bool isPrime(int n) { + if (n < 2) return false; + for (int i = 2; i * i <= n; i++) { + if (n % i == 0) return false; + } + return true; +} + +int main() { + int L, R, count = 0; + cin >> L >> R; + for (int x = L; x <= R; x++) { + if (isPrime(x)) count++; + } + cout << count; + return 0; +} +``` + +**易错提醒:** 1 不是素数;普通值传递不会修改主函数中的实参。 + +### 课堂3:统一格式输出【适中】 + +**题目描述:** 重载函数 `printLine`:一个版本接收一个整数并输出由该整数重复 5 次组成的一行;另一个版本接收一个字符和次数,输出指定长度的一行。两个函数都没有返回值。 + +**输入格式:** 依次输入一个整数、一个字符和字符的重复次数。 + +**输出格式:** 第一行输出 5 个 `x`;第二行输出 `n` 个字符 `c`。 + +**数据范围:** `0≤x≤9`,`1≤n≤50`。 + +**输入样例:** +```text +7 * 4 +``` + +**输出样例:** +```text +77777 +**** +``` + +**解题思路:** 两个函数同名但参数列表不同,构成函数重载;只负责输出,因此返回类型为 `void`。 + +**参考代码:** +```cpp +#include +using namespace std; + +void printLine(int x) { + for (int i = 0; i < 5; i++) cout << x; + cout << "\n"; +} + +void printLine(char c, int n) { + for (int i = 0; i < n; i++) cout << c; + cout << "\n"; +} + +int main() { + int x, n; + char c; + cin >> x >> c >> n; + printLine(x); + printLine(c, n); + return 0; +} +``` + +**易错提醒:** 重载依据参数数量或类型区分,不能只靠返回类型区分。 + +### 课堂4:全班累计加分【适中】 + +**题目描述:** 全局变量 `total` 表示全班累计加分。编写 `addScore(x)` 修改它;函数内部再定义同名局部变量 `total`,演示局部变量优先。每次调用后输出全局累计值。 + +**输入格式:** 第一行输入加分次数;第二行输入每次的加分值。 + +**输出格式:** 输出每次加分后的全局累计值。 + +**数据范围:** `1≤n≤20`,每次加分 `1~10`。 + +**输入样例:** +```text +3 +2 5 1 +``` + +**输出样例:** +```text +2 7 8 +``` + +**解题思路:** 用 `::total` 指明全局变量;局部变量仅在函数的大括号内有效。 + +**参考代码:** +```cpp +#include +using namespace std; + +int total = 0; + +void addScore(int x) { + int total = x; + ::total += total; +} + +int main() { + int n, x; + cin >> n; + for (int i = 0; i < n; i++) { + cin >> x; + addScore(x); + if (i) cout << " "; + cout << total; + } + return 0; +} +``` + +**易错提醒:** 局部变量会遮住同名全局变量;实际编程应尽量避免故意重名,本题只用于理解作用域。 + +## 二、课后作业(3题) + +### 课后1:求长方形面积【简单】 + +**题目描述:** 编写函数 `area(a,b)` 返回长方形面积。 + +**输入格式:** 输入两个正整数。 + +**输出格式:** 长方形面积。 + +**数据范围:** `1≤a,b≤10000`。 + +**输入样例:** +```text +4 6 +``` + +**输出样例:** +```text +24 +``` + +**解题思路:** 函数接收两个形参,返回它们的乘积。 + +**参考代码:** +```cpp +#include +using namespace std; + +int area(int a, int b) { + return a * b; +} + +int main() { + int a, b; + cin >> a >> b; + cout << area(a, b); + return 0; +} +``` + +**易错提醒:** 有返回值的函数不能漏写 `return`。 + +### 课后2:判断偶数【简单】 + +**题目描述:** 编写布尔函数 `isEven(n)`,偶数返回 `true`,奇数返回 `false`。主函数据此输出 `YES` 或 `NO`。 + +**输入格式:** 输入一个整数。 + +**输出格式:** `YES` 或 `NO`。 + +**数据范围:** `-10^9≤n≤10^9`。 + +**输入样例:** +```text +18 +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 判断 `n%2==0` 并直接返回结果。 + +**参考代码:** +```cpp +#include +using namespace std; + +bool isEven(int n) { + return n % 2 == 0; +} + +int main() { + int n; + cin >> n; + cout << (isEven(n) ? "YES" : "NO"); + return 0; +} +``` + +**易错提醒:** 判断相等使用 `==`,不是赋值符号 `=`。 + +### 课后3:打印分隔线【简单】 + +**题目描述:** 编写无返回值函数 `printLine(n)`,输出 `n` 个减号。 + +**输入格式:** 输入一个整数。 + +**输出格式:** 一行 `n` 个 `-`。 + +**数据范围:** `1≤n≤100`。 + +**输入样例:** +```text +5 +``` + +**输出样例:** +```text +----- +``` + +**解题思路:** 输出操作封装进 `void` 函数。 + +**参考代码:** +```cpp +#include +using namespace std; + +void printLine(int n) { + for (int i = 0; i < n; i++) cout << "-"; +} + +int main() { + int n; + cin >> n; + printLine(n); + return 0; +} +``` + +**易错提醒:** `void` 函数不能把调用结果赋给变量。 + +## 三、扩展练习(7题) + +### 扩展1:绝对值函数【基础综合】 + +**题目描述:** 不使用标准库 `abs`,编写函数 `myAbs(n)` 返回整数的绝对值。 + +**输入格式:** 输入一个整数。 + +**输出格式:** `n` 的绝对值。 + +**数据范围:** `-10^9≤n≤10^9`。 + +**输入样例:** +```text +-23 +``` + +**输出样例:** +```text +23 +``` + +**解题思路:** 负数返回 `-n`,否则返回 `n`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int myAbs(int n) { + if (n < 0) return -n; + return n; +} + +int main() { + int n; + cin >> n; + cout << myAbs(n); + return 0; +} +``` + +**易错提醒:** 每条可能的执行路线都应有返回值。 + +### 扩展2:数字各位和【基础综合】 + +**题目描述:** 编写函数 `digitSum(n)`,返回正整数各位数字之和。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** 各位数字之和。 + +**数据范围:** `1≤n≤10^9`。 + +**输入样例:** +```text +5026 +``` + +**输出样例:** +```text +13 +``` + +**解题思路:** 在函数内循环取个位并累加。 + +**参考代码:** +```cpp +#include +using namespace std; + +int digitSum(int n) { + int sum = 0; + while (n > 0) { + sum += n % 10; + n /= 10; + } + return sum; +} + +int main() { + int n; + cin >> n; + cout << digitSum(n); + return 0; +} +``` + +**易错提醒:** 函数内修改形参 `n` 不会改变主函数中的实参。 + +### 扩展3:最小公倍数【中等】 + +**题目描述:** 编写 `gcd(a,b)` 和 `lcm(a,b)` 两个函数,输出两数的最小公倍数。 + +**输入格式:** 输入两个正整数。 + +**输出格式:** 最小公倍数。 + +**数据范围:** `1≤a,b≤10^6`,答案不超过 `10^9`。 + +**输入样例:** +```text +12 18 +``` + +**输出样例:** +```text +36 +``` + +**解题思路:** 先求最大公约数,再利用 `lcm=a/gcd*b`,体现函数之间相互调用。 + +**参考代码:** +```cpp +#include +using namespace std; + +int gcd(int a, int b) { + while (b != 0) { + int r = a % b; + a = b; + b = r; + } + return a; +} + +long long lcm(int a, int b) { + return 1LL * a / gcd(a, b) * b; +} + +int main() { + int a, b; + cin >> a >> b; + cout << lcm(a, b); + return 0; +} +``` + +**易错提醒:** 先除后乘并使用 `long long`,可降低中间结果溢出的风险。 + +### 扩展4:重载求面积【中等】 + +**题目描述:** 重载函数 `area`:接收一个整数时返回正方形面积;接收两个整数时返回长方形面积。输入类型编号及边长后输出面积。 + +**输入格式:** 先输入图形类型;类型为 1 时再输入一个边长,类型为 2 时再输入长和宽。 + +**输出格式:** 图形面积。 + +**数据范围:** 边长为 `1~10000`。 + +**输入样例:** +```text +2 4 7 +``` + +**输出样例:** +```text +28 +``` + +**解题思路:** 用不同参数个数区分两个同名函数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int area(int side) { + return side * side; +} + +int area(int length, int width) { + return length * width; +} + +int main() { + int type, a, b; + cin >> type >> a; + if (type == 1) cout << area(a); + else { + cin >> b; + cout << area(a, b); + } + return 0; +} +``` + +**易错提醒:** 两个重载版本不能拥有完全相同的参数列表。 + +### 扩展5:回文数函数【中等】 + +**题目描述:** 编写函数 `isPalindrome(n)` 判断正整数是否为回文数,输出 `YES` 或 `NO`。 + +**输入格式:** 输入一个正整数。 + +**输出格式:** `YES` 或 `NO`。 + +**数据范围:** `1≤n≤10^9`。 + +**输入样例:** +```text +12321 +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 在函数中备份原数并构造反转数,最后比较。 + +**参考代码:** +```cpp +#include +using namespace std; + +bool isPalindrome(int n) { + int original = n; + int reversed = 0; + while (n > 0) { + reversed = reversed * 10 + n % 10; + n /= 10; + } + return reversed == original; +} + +int main() { + int n; + cin >> n; + cout << (isPalindrome(n) ? "YES" : "NO"); + return 0; +} +``` + +**易错提醒:** 必须备份原数;形参被拆完后会变成 0。 + +### 扩展6:连续素数对【略难】 + +**题目描述:** 两个相差 2 的素数称为孪生素数。编写 `isPrime`,输出 `[2,n]` 中所有孪生素数对,每对占一行;若没有,输出 `NONE`。 + +**输入格式:** 输入一个整数。 + +**输出格式:** 所有满足条件的 `p p+2`。 + +**数据范围:** `2≤n≤100000`。 + +**输入样例:** +```text +15 +``` + +**输出样例:** +```text +3 5 +5 7 +11 13 +``` + +**解题思路:** 枚举较小的数 `p`,同时调用两次 `isPrime`。 + +**参考代码:** +```cpp +#include +using namespace std; + +bool isPrime(int n) { + if (n < 2) return false; + for (int i = 2; i * i <= n; i++) { + if (n % i == 0) return false; + } + return true; +} + +int main() { + int n; + bool found = false; + cin >> n; + for (int p = 2; p + 2 <= n; p++) { + if (isPrime(p) && isPrime(p + 2)) { + cout << p << " " << p + 2 << "\n"; + found = true; + } + } + if (!found) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 枚举上界必须保证 `p+2≤n`;无解情况不能漏掉。 + +### 扩展7:函数流水线【略难】 + +**题目描述:** 编写三个函数:`digitSum(n)` 求各位和,`isPrime(n)` 判断素数,`countGood(L,R)` 统计区间中“各位和为素数”的整数个数。 + +**输入格式:** 输入区间的左端点和右端点。 + +**输出格式:** 满足条件的整数个数。 + +**数据范围:** `1≤L≤R≤100000`。 + +**输入样例:** +```text +10 15 +``` + +**输出样例:** +```text +3 +``` + +**解题思路:** 10~15 的各位和依次为 1~6,其中 2、3、5 是素数。让 `countGood` 调用 `digitSum`,再把结果交给 `isPrime`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int digitSum(int n) { + int sum = 0; + while (n > 0) { + sum += n % 10; + n /= 10; + } + return sum; +} + +bool isPrime(int n) { + if (n < 2) return false; + for (int i = 2; i * i <= n; i++) { + if (n % i == 0) return false; + } + return true; +} + +int countGood(int L, int R) { + int count = 0; + for (int x = L; x <= R; x++) { + if (isPrime(digitSum(x))) count++; + } + return count; +} + +int main() { + int L, R; + cin >> L >> R; + cout << countGood(L, R); + return 0; +} +``` + +**易错提醒:** 分清每个函数的单一职责;不要在多个位置重复编写同一段逻辑。 diff --git a/第04课-数据类型与分支结构/分支结构-编程题-教师版.md b/第04课-数据类型与分支结构/分支结构-编程题-教师版.md new file mode 100644 index 0000000..8d19469 --- /dev/null +++ b/第04课-数据类型与分支结构/分支结构-编程题-教师版.md @@ -0,0 +1,681 @@ +# 第4课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | GESP等级 | 对应课堂题 | +|---|---|---| +| `if / else if / else` 多层互斥分支 | 一级必考 | 课堂1 | +| 嵌套 `if`、逻辑运算与条件分组 | 一级必考 | 课堂2 | +| 普通打擂台、合法初始擂主 | 一级/二级 | 课堂3 | +| 二次打擂台、主条件与并列规则 | 二级 | 课堂4 | + +> 本课重点训练多层分支结构。14 题中有 10 题需要三层以上分支或嵌套判断。 + +## 一、课堂练习(4题) + +### 课堂1:阶梯电费【适中】 + +**题目描述:** 某月用电量不超过 180 度的部分每度 0.50 元;181~280 度的部分每度 0.60 元;超过 280 度的部分每度 0.80 元。若总电费超过 200 元再加收 5% 服务费。输出应付金额。 + +**输入格式:** 输入一个非负整数,表示本月用电量。 + +**输出格式:** 应付金额,保留两位小数。 + +**数据范围:** `0≤n≤10000`。 + +**输入样例:** +```text +300 +``` + +**输出样例:** +```text +166.00 +``` + +**解题思路:** 用多层 `if-else if-else` 分段累计;计算完成后再独立判断是否加收服务费。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n; + cin >> n; + double cost; + if (n <= 180) { + cost = n * 0.50; + } else if (n <= 280) { + cost = 180 * 0.50 + (n - 180) * 0.60; + } else { + cost = 180 * 0.50 + 100 * 0.60 + (n - 280) * 0.80; + } + if (cost > 200) cost *= 1.05; + cout << fixed << setprecision(2) << cost; + return 0; +} +``` + +**易错提醒:** 阶梯收费是分段累计,不是把全部用电量直接乘最高档单价。 + +### 课堂2:游乐园购票方案【适中】 + +**题目描述:** 输入年龄、身高和是否有成人陪同。身高低于 120 厘米免费;否则年龄不满 12 岁时,有成人陪同票价 30 元,无陪同不得入园;12~59 岁票价 80 元;60 岁及以上票价 40 元。周末所有非免费票加收 10 元。 + +**输入格式:** 依次输入年龄、身高、是否有成人陪同和是否为周末;后两项用 0 或 1 表示。 + +**输出格式:** 免费输出 `FREE`,不得入园输出 `DENIED`,否则输出票价。 + +**数据范围:** `1≤age≤120`,`50≤height≤220`。 + +**输入样例:** +```text +10 135 1 1 +``` + +**输出样例:** +```text +40 +``` + +**解题思路:** 先判断身高;不免费时再按年龄嵌套判断,儿童分支中继续判断陪同;最后对合法票价判断周末加价。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int age, height, accompanied, weekend; + cin >> age >> height >> accompanied >> weekend; + if (height < 120) { + cout << "FREE"; + } else { + int price; + if (age < 12) { + if (accompanied == 0) { + cout << "DENIED"; + return 0; + } + price = 30; + } else if (age < 60) { + price = 80; + } else { + price = 40; + } + if (weekend == 1) price += 10; + cout << price; + } + return 0; +} +``` + +**易错提醒:** “不得入园”不是票价 0;输出后应立即结束,避免继续加价。 + +### 课堂3:最佳选手【适中】 + +**题目描述:** 输入 `n` 名选手的分数,输出最高分和第一次取得该分数的选手编号。 + +**输入格式:** 第一行输入选手人数;第二行输入每名选手的分数。 + +**输出格式:** 最高分和选手编号(从 1 开始)。 + +**数据范围:** `1≤n≤100`,分数为 `0~100`。 + +**输入样例:** +```text +5 +88 95 90 95 70 +``` + +**输出样例:** +```text +95 2 +``` + +**解题思路:** 用第一名选手初始化擂主;只有出现严格更高分时才更新,便可保留最早编号。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, score, bestScore, bestId = 1; + cin >> n >> bestScore; + for (int id = 2; id <= n; id++) { + cin >> score; + if (score > bestScore) { + bestScore = score; + bestId = id; + } + } + cout << bestScore << " " << bestId; + return 0; +} +``` + +**易错提醒:** 擂主必须用第一个合法数据初始化;相等时不更新才能保留第一次出现的位置。 + +### 课堂4:最接近目标的候选人【适中】 + +**题目描述:** 输入目标分 `target` 以及 `n` 名候选人的编号和分数。选择与目标分距离最小者;距离相同时选分数较高者;仍相同则选编号较小者。 + +**输入格式:** 第一行输入候选人数和目标分;随后每行输入一名候选人的编号和分数。 + +**输出格式:** 入选者编号和分数。 + +**数据范围:** `1≤n≤100`,编号互不相同,分数为 `0~100`。 + +**输入样例:** +```text +4 80 +3 76 +8 84 +2 84 +5 90 +``` + +**输出样例:** +```text +2 84 +``` + +**解题思路:** 依次比较距离、分数、编号,三层优先级构成完整更新条件。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n, target, id, score, bestId, bestScore; + cin >> n >> target; + cin >> bestId >> bestScore; + for (int i = 1; i < n; i++) { + cin >> id >> score; + int d = abs(score - target); + int bestD = abs(bestScore - target); + if (d < bestD || + (d == bestD && score > bestScore) || + (d == bestD && score == bestScore && id < bestId)) { + bestId = id; + bestScore = score; + } + } + cout << bestId << " " << bestScore; + return 0; +} +``` + +**易错提醒:** 三层规则按先后顺序连接,每个次级规则都只能在前级相等时生效。 + +## 二、课后作业(3题) + +### 课后1:成绩等级【简单】 + +**题目描述:** 输入成绩:90~100 为 `A`,80~89 为 `B`,70~79 为 `C`,60~69 为 `D`,0~59 为 `E`;范围外输出 `ERROR`。 + +**输入格式:** 一个整数成绩。 + +**输出格式:** 等级或 `ERROR`。 + +**数据范围:** 输入整数绝对值不超过 1000。 + +**输入样例:** +```text +86 +``` + +**输出样例:** +```text +B +``` + +**解题思路:** 先排除非法范围,再从高到低使用多层 `else if`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int score; + cin >> score; + if (score < 0 || score > 100) cout << "ERROR"; + else if (score >= 90) cout << "A"; + else if (score >= 80) cout << "B"; + else if (score >= 70) cout << "C"; + else if (score >= 60) cout << "D"; + else cout << "E"; + return 0; +} +``` + +**易错提醒:** 从高到低判断后,不需要重复写上界。 + +### 课后2:月份天数【简单】 + +**题目描述:** 输入年份和月份,输出该月天数。闰年规则:能被 400 整除,或能被 4 整除但不能被 100 整除。 + +**输入格式:** 输入年份和月份。 + +**输出格式:** 天数;月份非法输出 `ERROR`。 + +**数据范围:** `1≤year≤9999`,月份可能为任意整数。 + +**输入样例:** +```text +2024 2 +``` + +**输出样例:** +```text +29 +``` + +**解题思路:** 先验证月份,再判断 2 月;2 月内部嵌套判断闰年,其余月份判断 30 天或 31 天。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int year, month; + cin >> year >> month; + if (month < 1 || month > 12) { + cout << "ERROR"; + } else if (month == 2) { + bool leap = year % 400 == 0 || + (year % 4 == 0 && year % 100 != 0); + cout << (leap ? 29 : 28); + } else if (month == 4 || month == 6 || + month == 9 || month == 11) { + cout << 30; + } else { + cout << 31; + } + return 0; +} +``` + +**易错提醒:** 闰年条件中的括号不能省略概念上的分组。 + +### 课后3:简单计算器【简单】 + +**题目描述:** 输入两个整数和运算符 `+ - * / %`,输出计算结果。除数为 0 时输出 `ERROR`,未知运算符输出 `UNKNOWN`。 + +**输入格式:** 依次输入第一个整数、运算符和第二个整数。 + +**输出格式:** 计算结果或错误信息。 + +**数据范围:** 两数绝对值不超过 10000。 + +**输入样例:** +```text +17 % 5 +``` + +**输出样例:** +```text +2 +``` + +**解题思路:** 先按运算符多层分支;进入除法或取模分支后再嵌套判断除数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int a, b; + char op; + cin >> a >> op >> b; + if (op == '+') cout << a + b; + else if (op == '-') cout << a - b; + else if (op == '*') cout << a * b; + else if (op == '/' || op == '%') { + if (b == 0) cout << "ERROR"; + else if (op == '/') cout << a / b; + else cout << a % b; + } else { + cout << "UNKNOWN"; + } + return 0; +} +``` + +**易错提醒:** 除法和取模都必须先检查除数是否为 0。 + +## 三、扩展练习(7题) + +### 扩展1:体质指数评价【基础综合】 + +**题目描述:** 输入体重(千克)和身高(米),计算 BMI。低于 18.5 输出 `LOW`,18.5~23.9 输出 `NORMAL`,24~27.9 输出 `HIGH`,28 及以上输出 `VERY HIGH`。 + +**输入格式:** 输入体重和身高两个实数。 + +**输出格式:** BMI 等级。 + +**数据范围:** `20≤weight≤300`,`1≤height≤2.5`。 + +**输入样例:** +```text +60 1.70 +``` + +**输出样例:** +```text +NORMAL +``` + +**解题思路:** 先计算 `weight/(height*height)`,再按从小到大的边界进行多层判断。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + double weight, height; + cin >> weight >> height; + double bmi = weight / (height * height); + if (bmi < 18.5) cout << "LOW"; + else if (bmi < 24) cout << "NORMAL"; + else if (bmi < 28) cout << "HIGH"; + else cout << "VERY HIGH"; + return 0; +} +``` + +**易错提醒:** 身高要平方;连续区间判断必须保证没有空档或重叠。 + +### 扩展2:三角形分类【中等】 + +**题目描述:** 输入三条边。不能组成三角形输出 `INVALID`;否则先按边分类输出 `EQUILATERAL`、`ISOSCELES` 或 `SCALENE`,再输出是否为直角三角形 `RIGHT` 或 `NOT RIGHT`。 + +**输入格式:** 输入三个正整数,表示三条边长。 + +**输出格式:** 两行分类结果。 + +**数据范围:** 边长不超过 10000。 + +**输入样例:** +```text +3 4 5 +``` + +**输出样例:** +```text +SCALENE +RIGHT +``` + +**解题思路:** 先判断合法性;合法后分别进行边型判断和直角判断。直角判断需覆盖三条边分别为斜边的情况。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + long long a, b, c; + cin >> a >> b >> c; + if (a + b <= c || a + c <= b || b + c <= a) { + cout << "INVALID"; + return 0; + } + if (a == b && b == c) cout << "EQUILATERAL\n"; + else if (a == b || a == c || b == c) cout << "ISOSCELES\n"; + else cout << "SCALENE\n"; + + if (a * a + b * b == c * c || + a * a + c * c == b * b || + b * b + c * c == a * a) cout << "RIGHT"; + else cout << "NOT RIGHT"; + return 0; +} +``` + +**易错提醒:** 必须先判断能否组成三角形;平方可能较大,应使用 `long long`。 + +### 扩展3:快递计费【中等】 + +**题目描述:** 基础运费按重量计算:不超过 1 千克 8 元;1~5 千克每增加 1 千克加 3 元,不足 1 千克按 1 千克算;超过 5 千克的部分每千克加 5 元。偏远地区加 10 元,加急再加总价的 20%。 + +**输入格式:** 依次输入包裹重量、是否为偏远地区和是否加急;后两项用 0 或 1 表示。 + +**输出格式:** 运费,保留两位小数。 + +**数据范围:** `0 +#include +#include +using namespace std; + +int main() { + double w; + int remote, urgent; + cin >> w >> remote >> urgent; + int kg = (int)ceil(w); + double cost; + if (kg <= 1) cost = 8; + else if (kg <= 5) cost = 8 + (kg - 1) * 3; + else cost = 8 + 4 * 3 + (kg - 5) * 5; + if (remote) cost += 10; + if (urgent) cost *= 1.2; + cout << fixed << setprecision(2) << cost; + return 0; +} +``` + +**易错提醒:** 示例中 6.2 千克按 7 千克计算;加急费在偏远附加费之后计算。 + +### 扩展4:石头剪刀布裁判【中等】 + +**题目描述:** 输入两名选手的手势 `R`(石头)、`S`(剪刀)、`P`(布)。非法手势输出 `ERROR`;相同输出 `DRAW`;否则输出 `A` 或 `B`。 + +**输入格式:** 两个字符。 + +**输出格式:** 裁判结果。 + +**数据范围:** 任意可见字符。 + +**输入样例:** +```text +R S +``` + +**输出样例:** +```text +A +``` + +**解题思路:** 先检查两个手势是否合法;再判断平局;最后用三个获胜组合判断 A 是否获胜。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char a, b; + cin >> a >> b; + bool validA = a == 'R' || a == 'S' || a == 'P'; + bool validB = b == 'R' || b == 'S' || b == 'P'; + if (!validA || !validB) cout << "ERROR"; + else if (a == b) cout << "DRAW"; + else if ((a == 'R' && b == 'S') || + (a == 'S' && b == 'P') || + (a == 'P' && b == 'R')) cout << "A"; + else cout << "B"; + return 0; +} +``` + +**易错提醒:** 合法性判断必须放在胜负判断之前。 + +### 扩展5:奖学金评定【中等】 + +**题目描述:** 输入平均分、竞赛获奖次数、志愿服务时长和是否违纪。违纪直接输出 `NONE`;否则平均分≥90 且获奖≥2 为一等奖,平均分≥85 且(获奖≥1 或服务≥50)为二等奖,平均分≥80 或服务≥100 为三等奖,其余无奖。 + +**输入格式:** 依次输入平均分、获奖次数、志愿服务时长和是否违纪。 + +**输出格式:** `FIRST`、`SECOND`、`THIRD` 或 `NONE`。 + +**数据范围:** 平均分 `0~100`,其余为非负整数,违纪用 0/1 表示。 + +**输入样例:** +```text +87 0 60 0 +``` + +**输出样例:** +```text +SECOND +``` + +**解题思路:** 违纪是最高优先级;未违纪时按奖项从高到低判断,括号明确“且”“或”的组合。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int average, awards, service, violation; + cin >> average >> awards >> service >> violation; + if (violation) cout << "NONE"; + else if (average >= 90 && awards >= 2) cout << "FIRST"; + else if (average >= 85 && (awards >= 1 || service >= 50)) + cout << "SECOND"; + else if (average >= 80 || service >= 100) cout << "THIRD"; + else cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 一定要从高等级奖项向低等级判断,否则较宽条件会抢先匹配。 + +### 扩展6:最佳商品【略难】 + +**题目描述:** 输入 `n` 件商品的编号、价格和评分。选择评分最高的商品;评分相同时价格更低者优先;仍相同则编号更小者优先。 + +**输入格式:** 第一行输入商品数量;随后每行输入一件商品的编号、价格和评分。 + +**输出格式:** 最佳商品编号。 + +**数据范围:** `1≤n≤100`,价格和评分为正整数。 + +**输入样例:** +```text +4 +5 100 9 +2 120 9 +8 80 8 +3 100 9 +``` + +**输出样例:** +```text +3 +``` + +**解题思路:** 第一优先级比较评分,第二优先级比较价格,第三优先级比较编号。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, id, price, score; + int bestId, bestPrice, bestScore; + cin >> n >> bestId >> bestPrice >> bestScore; + for (int i = 1; i < n; i++) { + cin >> id >> price >> score; + if (score > bestScore || + (score == bestScore && price < bestPrice) || + (score == bestScore && price == bestPrice && id < bestId)) { + bestId = id; + bestPrice = price; + bestScore = score; + } + } + cout << bestId; + return 0; +} +``` + +**易错提醒:** 不要用任意极值初始化三个擂台变量,直接读入第一件商品最安全。 + +### 扩展7:综合录取规则【略难】 + +**题目描述:** 输入笔试、面试、体育三项成绩,以及是否有竞赛一等奖。任一成绩不在 0~100 输出 `ERROR`。竞赛一等奖且笔试≥70 可直接录取;否则笔试≥85 时,面试≥80 且体育≥60 录取;笔试 75~84 时,面试≥90 且体育≥70 才录取;其他情况不录取。 + +**输入格式:** 依次输入笔试成绩、面试成绩、体育成绩和是否获得竞赛一等奖;最后一项用 0 或 1 表示。 + +**输出格式:** `ADMITTED`、`REJECTED` 或 `ERROR`。 + +**数据范围:** 成绩可能为任意整数。 + +**输入样例:** +```text +78 92 75 0 +``` + +**输出样例:** +```text +ADMITTED +``` + +**解题思路:** 先做数据合法性检查,再判断特殊录取,最后按笔试档位嵌套判断面试和体育条件。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int written, interview, sport, prize; + cin >> written >> interview >> sport >> prize; + if (written < 0 || written > 100 || + interview < 0 || interview > 100 || + sport < 0 || sport > 100) { + cout << "ERROR"; + } else if (prize == 1 && written >= 70) { + cout << "ADMITTED"; + } else if (written >= 85) { + if (interview >= 80 && sport >= 60) cout << "ADMITTED"; + else cout << "REJECTED"; + } else if (written >= 75) { + if (interview >= 90 && sport >= 70) cout << "ADMITTED"; + else cout << "REJECTED"; + } else { + cout << "REJECTED"; + } + return 0; +} +``` + +**易错提醒:** 数据检查、特殊规则、普通规则的优先级不能调换。 diff --git a/第04课-数据类型与分支结构/教案-学生版.md b/第04课-数据类型与分支结构/教案-学生版.md new file mode 100644 index 0000000..a140f59 --- /dev/null +++ b/第04课-数据类型与分支结构/教案-学生版.md @@ -0,0 +1,289 @@ +# 第4课:数据类型、ASCII码与分支结构 + +## 教学目标 +- 掌握五种常用数据类型:int、char、double、bool、string +- 理解 char 的 ASCII 码本质 +- 能用 ASCII 码判断和转换字符类型 +- 掌握 double 的格式化输出 +- 掌握输入输出、运算符、类型转换和多层分支 +- 认识 switch、三目运算和打擂台 + +## 核心考点:数据类型、类型转换与数据范围 + +### 考什么 +选择正确的________,判断表达式结果,完成字符与整数转换,并识别溢出或精度问题。 + +### 必须理解 +两个整数相除会进行________除法,小数部分会________。`char` 内部保存字符对应的________码。较大整数通常使用________。 + +### 核心写法 +```cpp +double avg = ________ * sum / n; +int d = c - ________; +char c = d + ________; +long long mul = ________ * a * b; +``` + +### 常见错误 +- 为什么 `double avg = sum / n;` 仍可能丢失小数?________ +- 字符 `'5'` 和整数 `5` 是否相同?________________ +- 怎样避免 `a * b` 在赋值前已经溢出?________________ + +### 判断是否掌握 +- [ ] 我能根据范围选择数据类型。 +- [ ] 我能判断整数除法和类型转换结果。 +- [ ] 我能转换数字字符与整数。 + +--- + +## 一级、二级核心:运算、转换与分支 + +### 类型转换 + +```cpp +double avg = ______ * sum / cnt; +int num = (int)ch; +char ch = (char)num; +``` + +整数除法会不会保留小数:______。 + +### 多层分支 + +```cpp +if (score >= 90) { + cout << "A"; +} else if (score >= 80) { + cout << "B"; +} else { + cout << "C"; +} +``` + +### `switch` 语句 + +```cpp +switch (op) { + case 1: cout << "加法"; ______; + case 2: cout << "减法"; ______; + default: cout << "无效选择"; +} +``` + +### 三目运算 + +```cpp +int ma = (a > b) ? ______ : ______; +``` + +### 二次打擂台 + +先比较 ______,相同时再比较 ______。擂主应使用第一个合法候选值初始化。 + +## 课堂编程题 + +### 数据类型部分 + +#### 课堂1:超大长方体体积【适中】 + +**题目描述:** 输入长方体的长、宽、高,输出体积。三个量都能用 `int` 保存,但乘积可能超过 `int` 范围。 + +**输入格式:** 输入三个正整数,依次表示长、宽和高。 + +**输出格式:** 长方体体积。 + +**数据范围:** `1≤a,b,c≤10^6`,答案不超过 `10^18`。 + +**输入样例:** +```text +100000 100000 2 +``` + +**输出样例:** +```text +20000000000 +``` + +**解题思路:** 用 `1LL*a*b*c` 使第一次乘法就按 `long long` 计算。 + +**易错提醒:** 只把结果变量写成 `long long` 不够,右侧的乘法也必须提前转换。 + +#### 课堂2:比赛平均分【适中】 + +**题目描述:** 输入三名选手的总分和比赛场数,分别输出三人的平均分,保留两位小数;最后输出最高平均分。 + +**输入格式:** 依次输入三名选手的总分和比赛场数。 + +**输出格式:** 第一行三个平均分;第二行最高平均分,均保留两位小数。 + +**数据范围:** `1≤n≤100`,`0≤s1,s2,s3≤10000`。 + +**输入样例:** +```text +25 20 29 3 +``` + +**输出样例:** +```text +8.33 6.67 9.67 +9.67 +``` + +**解题思路:** 用 `1.0*s/n` 触发浮点除法,使用 `fixed` 和 `setprecision(2)`。 + +**易错提醒:** `double a=s1/n` 仍会先做整数除法,小数已经丢失。 + +#### 课堂3:字母变身与平移【适中】 + +**题目描述:** 输入一个英文字母和整数 `k`。先把字母转换为相反大小写,再在保持新大小写的前提下向后循环平移 `k` 位。 + +**输入格式:** 输入一个英文字母和向后平移的位数。 + +**输出格式:** 转换并平移后的字母。 + +**数据范围:** `0≤k≤10^9`。 + +**输入样例:** +```text +Y 3 +``` + +**输出样例:** +```text +b +``` + +**解题思路:** 大小写互转可利用 ASCII 差值;平移只需使用 `k%26`,公式为 `(c-base+k)%26+base`。 + +**易错提醒:** 平移必须循环;不能直接输出 `c+k`,否则可能越过字母范围。 + +#### 课堂4:账号检查器【适中】 + +**题目描述:** 输入一个不含空格的字符串。若长度不少于 6,且同时含有大写字母、小写字母和数字,输出 `YES`,否则输出 `NO`。 + +**输入格式:** 一个字符串。 + +**输出格式:** `YES` 或 `NO`。 + +**数据范围:** 字符串长度不超过 100。 + +**输入样例:** +```text +Abc123 +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 用三个 `bool` 变量记录是否出现三类字符,遍历 `string` 后综合判断。 + +**易错提醒:** `bool` 只记录真假;三个条件必须同时满足,应使用 `&&`。 + +### 分支结构部分 + +#### 课堂5:阶梯电费【适中】 + +**题目描述:** 某月用电量不超过 180 度的部分每度 0.50 元;181~280 度的部分每度 0.60 元;超过 280 度的部分每度 0.80 元。若总电费超过 200 元再加收 5% 服务费。输出应付金额。 + +**输入格式:** 输入一个非负整数,表示本月用电量。 + +**输出格式:** 应付金额,保留两位小数。 + +**数据范围:** `0≤n≤10000`。 + +**输入样例:** +```text +300 +``` + +**输出样例:** +```text +166.00 +``` + +**解题思路:** 用多层 `if-else if-else` 分段累计;计算完成后再独立判断是否加收服务费。 + +**易错提醒:** 阶梯收费是分段累计,不是把全部用电量直接乘最高档单价。 + +#### 课堂6:游乐园购票方案【适中】 + +**题目描述:** 输入年龄、身高和是否有成人陪同。身高低于 120 厘米免费;否则年龄不满 12 岁时,有成人陪同票价 30 元,无陪同不得入园;12~59 岁票价 80 元;60 岁及以上票价 40 元。周末所有非免费票加收 10 元。 + +**输入格式:** 依次输入年龄、身高、是否有成人陪同和是否为周末;后两项用 0 或 1 表示。 + +**输出格式:** 免费输出 `FREE`,不得入园输出 `DENIED`,否则输出票价。 + +**数据范围:** `1≤age≤120`,`50≤height≤220`。 + +**输入样例:** +```text +10 135 1 1 +``` + +**输出样例:** +```text +40 +``` + +**解题思路:** 先判断身高;不免费时再按年龄嵌套判断,儿童分支中继续判断陪同;最后对合法票价判断周末加价。 + +**易错提醒:** "不得入园"不是票价 0;输出后应立即结束,避免继续加价。 + +#### 课堂7:最佳选手【适中】 + +**题目描述:** 输入 `n` 名选手的分数,输出最高分和第一次取得该分数的选手编号。 + +**输入格式:** 第一行输入选手人数;第二行输入每名选手的分数。 + +**输出格式:** 最高分和选手编号(从 1 开始)。 + +**数据范围:** `1≤n≤100`,分数为 `0~100`。 + +**输入样例:** +```text +5 +88 95 90 95 70 +``` + +**输出样例:** +```text +95 2 +``` + +**解题思路:** 用第一名选手初始化擂主;只有出现严格更高分时才更新,便可保留最早编号。 + +**易错提醒:** 擂主必须用第一个合法数据初始化;相等时不更新才能保留第一次出现的位置。 + +#### 课堂8:最接近目标的候选人【适中】 + +**题目描述:** 输入目标分 `target` 以及 `n` 名候选人的编号和分数。选择与目标分距离最小者;距离相同时选分数较高者;仍相同则选编号较小者。 + +**输入格式:** 第一行输入候选人数和目标分;随后每行输入一名候选人的编号和分数。 + +**输出格式:** 入选者编号和分数。 + +**数据范围:** `1≤n≤100`,编号互不相同,分数为 `0~100`。 + +**输入样例:** +```text +4 80 +3 76 +8 84 +2 84 +5 90 +``` + +**输出样例:** +```text +2 84 +``` + +**解题思路:** 依次比较距离、分数、编号,三层优先级构成完整更新条件。 + +**易错提醒:** 三层规则按先后顺序连接,每个次级规则都只能在前级相等时生效。 + +## 课后作业 + +> 请完成本课对应的课后作业,见编程题文件。 diff --git a/第04课-数据类型与分支结构/教案-教师版.md b/第04课-数据类型与分支结构/教案-教师版.md new file mode 100644 index 0000000..ec00d6d --- /dev/null +++ b/第04课-数据类型与分支结构/教案-教师版.md @@ -0,0 +1,283 @@ +# 第4课:数据类型、ASCII码与分支结构 + +## 教学目标 +- 掌握五种常用数据类型:int、char、double、bool、string +- 理解 char 的 ASCII 码本质 +- 能用 ASCII 码判断和转换字符类型 +- 掌握 double 的格式化输出 +- 掌握输入输出、算术/关系/逻辑运算与类型转换 +- 掌握 if、else if、else、switch 和三目运算 +- 掌握普通打擂台与带并列规则的二次打擂台 + +## 核心考点:数据类型、类型转换与数据范围 + +### 考什么 +考试会要求判断变量应使用哪种类型、计算表达式结果、完成字符与整数转换,或分析整数溢出和小数精度。真正的重点是:数据以什么形式存储,会直接影响运算结果。 + +### 必须理解 +`int` 存整数,范围有限;较大的整数应使用 `long long`。`double` 存小数,但可能存在微小精度误差。`char` 表面是字符,内部保存对应的 ASCII 整数,因此 `'7' - '0'` 得到整数 7。两个整数相除仍做整数除法,小数部分会被截掉;只要一边先转成 `double`,才会保留小数。赋值或运算时发生类型转换,可能扩大范围,也可能丢失信息。 + +### 核心写法 +```cpp +double average = 1.0 * sum / n; // 先变成浮点运算 +int digit = c - '0'; // 数字字符转整数 +char c = digit + '0'; // 0~9 转数字字符 +long long product = 1LL * a * b; +``` +`1.0` 触发浮点运算,`1LL` 让乘法从一开始就在 `long long` 范围内进行。 + +### 常见错误 +- `double average = sum / n;` 中右边先做整数除法,赋给 double 时小数已经丢失。 +- 把字符 `'5'` 当成整数 5;它们的存储值不同。 +- 结果变量使用 `long long`,但 `a * b` 已先按 int 溢出。 +- 用 `==` 直接比较复杂浮点计算结果,忽略精度误差。 + +### 判断是否掌握 +能根据数据范围选择 `int`、`long long` 或 `double`,能手算整数除法与类型转换结果,并能完成数字字符和整数之间的转换。 + +--- + +## 一级、二级核心:运算、转换与分支 + +### 输入输出与运算符 + +一级要求熟练使用 `cin`、`cout`,掌握算术、关系和逻辑运算: + +```cpp +int a, b; +cin >> a >> b; +cout << a + b << endl; + +bool valid = (a >= 0 && a <= 100); +``` + +整数除法会丢弃小数部分;需要小数时,应让至少一个运算数先转成 `double`。 + +### 隐式转换与强制转换 + +```cpp +double average = 1.0 * sum / count; // 隐式转换为 double +int code = (int)letter; // 强制转换为 int +char letter = (char)code; // 强制转换为 char +``` + +类型范围变大通常较安全;从浮点数转成整数会丢弃小数部分。 + +### 多层分支 + +```cpp +if (score >= 90) { + cout << "A"; +} else if (score >= 80) { + cout << "B"; +} else if (score >= 60) { + cout << "C"; +} else { + cout << "D"; +} +``` + +多个条件互斥时使用 `else if`,避免多个独立 `if` 重复执行。 + +### `switch` 语句 + +```cpp +switch (choice) { + case 1: cout << "加法"; break; + case 2: cout << "减法"; break; + default: cout << "无效选择"; +} +``` + +`switch` 适合根据一个整数或字符的不同取值进行分支。每个 `case` 后通常需要 `break`,否则会继续执行后面的分支。 + +### 三目运算 + +```cpp +int bigger = (a > b) ? a : b; +``` + +三目运算适合简单的二选一,不适合代替复杂的多层分支。 + +### 打擂台与二次规则 + +寻找最接近目标值的数时,先比较距离;距离相同再比较数值: + +```cpp +if (abs(x - target) < abs(best - target) || + (abs(x - target) == abs(best - target) && x < best)) { + best = x; +} +``` + +擂主必须用第一个合法候选值初始化,不能随意设为 0。 + +## 题目一:判断字符类型并输出ASCII码 + +### 核心代码(一句话) +> 字符类型判断的核心:**char 本质是整数(ASCII码),可以在字符范围和整数之间自由比较和转换。** + +### 自然语言思路 +键盘上敲的每个字符,在计算机里都存成了一个整数——这就是 **ASCII 码**。 + +记住三个关键 ASCII 码: +- `'0'` = 48,`'1'`~`'9'` 依次递增 +- `'A'` = 65,`'B'`~`'Z'` 依次递增 +- `'a'` = 97,`'b'`~`'z'` 依次递增 + +所以判断一个字符是什么类型,有两种方法: + +**方法一(看 ASCII 码范围)**: +- 如果 `c` 在 `'A'` ~ `'Z'` 之间(即 65~90)→ 大写字母 +- 如果 `c` 在 `'a'` ~ `'z'` 之间(即 97~122)→ 小写字母 +- 如果 `c` 在 `'0'` ~ `'9'` 之间(即 48~57)→ 数字 +- 否则 → 其他字符 + +**方法二(用第1课学的自带函数)**: +- `isupper(c)` → 大写字母 +- `islower(c)` → 小写字母 +- `isdigit(c)` → 数字 + +最后,`int(c)` 或 `(int)c` 直接输出字符对应的 ASCII 码值。 + +### 伪代码 +``` +输入字符 ch + +如果 ch 在 'A' ~ 'Z' 之间: + 输出 "大写字母" +否则如果 ch 在 'a' ~ 'z' 之间: + 输出 "小写字母" +否则如果 ch 在 '0' ~ '9' 之间: + 输出 "数字" +否则: + 输出 "其他字符" + +输出 "ASCII码 = " + int(ch) +``` + +### 真实代码 +```cpp +#include +#include // isupper、islower、isdigit +using namespace std; + +int main() { + char ch; + cin >> ch; + + // 方法一:ASCII 码范围判断 + if (ch >= 'A' && ch <= 'Z') { + cout << "大写字母" << endl; + } else if (ch >= 'a' && ch <= 'z') { + cout << "小写字母" << endl; + } else if (ch >= '0' && ch <= '9') { + cout << "数字" << endl; + } else { + cout << "其他字符" << endl; + } + + // 输出 ASCII 码值 + cout << "ASCII码 = " << (int)ch << endl; + + return 0; +} +``` + +### 方法二(用自带函数,更简洁) +```cpp +if (isupper(ch)) { + cout << "大写字母" << endl; +} else if (islower(ch)) { + cout << "小写字母" << endl; +} else if (isdigit(ch)) { + cout << "数字" << endl; +} else { + cout << "其他字符" << endl; +} +``` + +### 注意点 +1. **`char` 能直接比大小**:`ch >= 'A'` 就是在比较 ASCII 码值,不需要先转成 int。 +2. **字符写单引号**:`'A'` 不是 `"A"`,单引号是 char,双引号是字符串。 +3. **ASCII 码值记忆技巧**:`'0'`=48,`'A'`=65,`'a'`=97。小写字母 = 大写字母 + 32。 +4. **大小写互转**:大写转小写 `ch + 32`,小写转大写 `ch - 32`。或者用 `tolower(ch)` / `toupper(ch)`(返回 int,需要转回 char)。 +5. **不要混用 string 和 char**:`string` 是字符串(双引号),`char` 是单个字符(单引号),两者不同。 + +--- + +## 题目二:(待橙子补充) + +> 建议覆盖:double 格式化输出、int vs long long 范围等 + +--- + +### 核心代码(一句话) +> + +### 自然语言思路 + + +### 伪代码 + + +### 真实代码 +```cpp + +``` + +### 注意点 + + +--- + +## 题目三:(待橙子补充) + +> 建议覆盖:bool 类型、string 基本操作等 + +--- + +### 核心代码(一句话) +> + +### 自然语言思路 + + +### 伪代码 + + +### 真实代码 +```cpp + +``` + +### 注意点 + + +--- + +## 本课打油诗 + +> int 存整数范围广,char 单字符本质藏。 +> double 小数保精度,bool 对错分两旁。 +> string 处理字符串,五种类型记心上。 + +--- + +## ASCII 码速查表(常用) + +| 字符 | ASCII 码 | +|------|----------| +| `'0'` ~ `'9'` | 48 ~ 57 | +| `'A'` ~ `'Z'` | 65 ~ 90 | +| `'a'` ~ `'z'` | 97 ~ 122 | +| 空格 `' '` | 32 | + +> 记忆口诀:**4A = 65**,不是"4个A",是"数字4开头,A是65"。小写比大写多32。 + +--- + +## 课后作业 +(待补充) +1. +2. diff --git a/第04课-数据类型与分支结构/数据类型-编程题-教师版.md b/第04课-数据类型与分支结构/数据类型-编程题-教师版.md new file mode 100644 index 0000000..6fb3283 --- /dev/null +++ b/第04课-数据类型与分支结构/数据类型-编程题-教师版.md @@ -0,0 +1,581 @@ +# 第3课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | GESP等级 | 对应课堂题 | +|---|---|---| +| `int`、`long long`、数据范围与 `1LL` | 一级必考 | 课堂1 | +| `double`、整数除法、类型转换、格式化输出 | 一级必考 | 课堂2 | +| `char`、ASCII 码、大小写转换、字母平移 | 一级/二级必考 | 课堂3 | +| `bool`、`string`、字符与数字转换 | 一级/二级 | 课堂4 | + +> 本课加强大小写转换和字母平移训练。字母平移均按英文字母表循环,即 `Z` 的下一个字母是 `A`。 + +## 一、课堂练习(4题) + +### 课堂1:超大长方体体积【适中】 + +**题目描述:** 输入长方体的长、宽、高,输出体积。三个量都能用 `int` 保存,但乘积可能超过 `int` 范围。 + +**输入格式:** 输入三个正整数,依次表示长、宽和高。 + +**输出格式:** 长方体体积。 + +**数据范围:** `1≤a,b,c≤10^6`,答案不超过 `10^18`。 + +**输入样例:** +```text +100000 100000 2 +``` + +**输出样例:** +```text +20000000000 +``` + +**解题思路:** 用 `1LL*a*b*c` 使第一次乘法就按 `long long` 计算。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int a, b, c; + cin >> a >> b >> c; + long long volume = 1LL * a * b * c; + cout << volume; + return 0; +} +``` + +**易错提醒:** 只把结果变量写成 `long long` 不够,右侧的乘法也必须提前转换。 + +### 课堂2:比赛平均分【适中】 + +**题目描述:** 输入三名选手的总分和比赛场数,分别输出三人的平均分,保留两位小数;最后输出最高平均分。 + +**输入格式:** 依次输入三名选手的总分和比赛场数。 + +**输出格式:** 第一行三个平均分;第二行最高平均分,均保留两位小数。 + +**数据范围:** `1≤n≤100`,`0≤s1,s2,s3≤10000`。 + +**输入样例:** +```text +25 20 29 3 +``` + +**输出样例:** +```text +8.33 6.67 9.67 +9.67 +``` + +**解题思路:** 用 `1.0*s/n` 触发浮点除法,使用 `fixed` 和 `setprecision(2)`。 + +**参考代码:** +```cpp +#include +#include +#include +using namespace std; + +int main() { + int s1, s2, s3, n; + cin >> s1 >> s2 >> s3 >> n; + double a = 1.0 * s1 / n; + double b = 1.0 * s2 / n; + double c = 1.0 * s3 / n; + cout << fixed << setprecision(2); + cout << a << " " << b << " " << c << "\n"; + cout << max(a, max(b, c)); + return 0; +} +``` + +**易错提醒:** `double a=s1/n` 仍会先做整数除法,小数已经丢失。 + +### 课堂3:字母变身与平移【适中】 + +**题目描述:** 输入一个英文字母和整数 `k`。先把字母转换为相反大小写,再在保持新大小写的前提下向后循环平移 `k` 位。 + +**输入格式:** 输入一个英文字母和向后平移的位数。 + +**输出格式:** 转换并平移后的字母。 + +**数据范围:** `0≤k≤10^9`。 + +**输入样例:** +```text +Y 3 +``` + +**输出样例:** +```text +b +``` + +**解题思路:** 大小写互转可利用 ASCII 差值;平移只需使用 `k%26`,公式为 `(c-base+k)%26+base`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char c; + long long k; + cin >> c >> k; + if (c >= 'A' && c <= 'Z') c = c + ('a' - 'A'); + else c = c - ('a' - 'A'); + char base = (c >= 'a' && c <= 'z') ? 'a' : 'A'; + c = (c - base + k % 26) % 26 + base; + cout << c; + return 0; +} +``` + +**易错提醒:** 平移必须循环;不能直接输出 `c+k`,否则可能越过字母范围。 + +### 课堂4:账号检查器【适中】 + +**题目描述:** 输入一个不含空格的字符串。若长度不少于 6,且同时含有大写字母、小写字母和数字,输出 `YES`,否则输出 `NO`。 + +**输入格式:** 一个字符串。 + +**输出格式:** `YES` 或 `NO`。 + +**数据范围:** 字符串长度不超过 100。 + +**输入样例:** +```text +Abc123 +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 用三个 `bool` 变量记录是否出现三类字符,遍历 `string` 后综合判断。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + bool upper = false, lower = false, digit = false; + for (char c : s) { + if (c >= 'A' && c <= 'Z') upper = true; + else if (c >= 'a' && c <= 'z') lower = true; + else if (c >= '0' && c <= '9') digit = true; + } + bool valid = s.size() >= 6 && upper && lower && digit; + cout << (valid ? "YES" : "NO"); + return 0; +} +``` + +**易错提醒:** `bool` 只记录真假;三个条件必须同时满足,应使用 `&&`。 + +## 二、课后作业(3题) + +### 课后1:小写转大写【简单】 + +**题目描述:** 输入一个小写字母,使用 ASCII 码运算转成大写字母。 + +**输入格式:** 一个小写字母。 + +**输出格式:** 对应的大写字母。 + +**数据范围:** `a~z`。 + +**输入样例:** +```text +g +``` + +**输出样例:** +```text +G +``` + +**解题思路:** 小写字母比对应大写字母的 ASCII 码大 32,计算 `c-'a'+'A'`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char c; + cin >> c; + cout << (char)(c - 'a' + 'A'); + return 0; +} +``` + +**易错提醒:** 字符常量必须使用单引号。 + +### 课后2:数字字符还原【简单】 + +**题目描述:** 输入一个数字字符,输出它代表的整数再加 5 的结果。 + +**输入格式:** 输入一个范围为 0~9 的数字字符。 + +**输出格式:** 一个整数。 + +**数据范围:** `'0'≤c≤'9'`。 + +**输入样例:** +```text +7 +``` + +**输出样例:** +```text +12 +``` + +**解题思路:** 使用 `c-'0'` 把数字字符转换成整数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char c; + cin >> c; + int number = c - '0'; + cout << number + 5; + return 0; +} +``` + +**易错提醒:** 直接计算 `c+5` 得到的是新的字符编码,不是题目需要的整数。 + +### 课后3:整数平均数【简单】 + +**题目描述:** 输入两个整数,输出它们的平均数,保留一位小数。 + +**输入格式:** 输入两个整数。 + +**输出格式:** 平均数,保留一位小数。 + +**数据范围:** 两数绝对值不超过 `10^6`。 + +**输入样例:** +```text +2 5 +``` + +**输出样例:** +```text +3.5 +``` + +**解题思路:** 用 `(a+b)/2.0` 进行浮点除法。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int a, b; + cin >> a >> b; + cout << fixed << setprecision(1) << (a + b) / 2.0; + return 0; +} +``` + +**易错提醒:** 除数写成 `2` 会进行整数除法,写成 `2.0` 才保留小数。 + +## 三、扩展练习(7题) + +### 扩展1:向后移动一位【基础综合】 + +**题目描述:** 输入一个小写字母,输出字母表中的下一个小写字母;`z` 的下一个字母为 `a`。 + +**输入格式:** 一个小写字母。 + +**输出格式:** 平移后的字母。 + +**数据范围:** `a~z`。 + +**输入样例:** +```text +z +``` + +**输出样例:** +```text +a +``` + +**解题思路:** 用 `(c-'a'+1)%26+'a'` 实现循环。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char c; + cin >> c; + cout << (char)((c - 'a' + 1) % 26 + 'a'); + return 0; +} +``` + +**易错提醒:** 单独写 `c+1` 无法处理 `z`。 + +### 扩展2:双向字母平移【基础综合】 + +**题目描述:** 输入一个大写字母、方向 `L` 或 `R` 和步数 `k`。向左或向右循环平移,输出结果。 + +**输入格式:** 依次输入一个大写字母、平移方向和移动位数。 + +**输出格式:** 平移后的大写字母。 + +**数据范围:** `0≤k≤10^9`。 + +**输入样例:** +```text +B L 3 +``` + +**输出样例:** +```text +Y +``` + +**解题思路:** 右移加 `k`;左移等价于加 `26-k%26`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char c, direction; + long long k; + cin >> c >> direction >> k; + k %= 26; + if (direction == 'L') k = (26 - k) % 26; + cout << (char)((c - 'A' + k) % 26 + 'A'); + return 0; +} +``` + +**易错提醒:** C++ 中负数取模可能仍为负数,所以左移时先转成非负偏移量。 + +### 扩展3:两个字母的距离【中等】 + +**题目描述:** 输入两个同为大写的字母,输出从第一个字母向后循环移动到第二个字母所需的最少步数。 + +**输入格式:** 输入两个大写字母。 + +**输出格式:** 向后平移步数。 + +**数据范围:** `A~Z`。 + +**输入样例:** +```text +X C +``` + +**输出样例:** +```text +5 +``` + +**解题思路:** 计算 `(b-a+26)%26`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char a, b; + cin >> a >> b; + cout << (b - a + 26) % 26; + return 0; +} +``` + +**易错提醒:** 加 26 是为了避免结果为负数。 + +### 扩展4:字母镜像【中等】 + +**题目描述:** 字母表两端互为镜像:`A↔Z`、`B↔Y`,小写字母同理。输入一个字母,输出它的镜像字母,并保持大小写。 + +**输入格式:** 一个英文字母。 + +**输出格式:** 镜像字母。 + +**数据范围:** 英文字母。 + +**输入样例:** +```text +C +``` + +**输出样例:** +```text +X +``` + +**解题思路:** 大写字母镜像为 `'A'+'Z'-c`,小写同理。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char c; + cin >> c; + if (c >= 'A' && c <= 'Z') cout << (char)('A' + 'Z' - c); + else cout << (char)('a' + 'z' - c); + return 0; +} +``` + +**易错提醒:** 大写和小写必须使用各自的范围计算。 + +### 扩展5:凯撒口令【中等】 + +**题目描述:** 输入一个不含空格的字符串和非负整数 `k`。所有英文字母向后循环平移 `k` 位,保持大小写;数字和符号不变。 + +**输入格式:** 第一行输入一个不含空格的字符串;第二行输入向后平移的位数。 + +**输出格式:** 平移后的字符串。 + +**数据范围:** 字符串长度不超过 100,`0≤k≤10^9`。 + +**输入样例:** +```text +Az-9 +2 +``` + +**输出样例:** +```text +Cb-9 +``` + +**解题思路:** 根据字符范围选择基准 `'A'` 或 `'a'`;其他字符原样保留。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + long long k; + cin >> s >> k; + k %= 26; + for (char &c : s) { + if (c >= 'A' && c <= 'Z') + c = (c - 'A' + k) % 26 + 'A'; + else if (c >= 'a' && c <= 'z') + c = (c - 'a' + k) % 26 + 'a'; + } + cout << s; + return 0; +} +``` + +**易错提醒:** 平移后必须写回字符串;数字和符号不能参与字母公式。 + +### 扩展6:最短平移方向【略难】 + +**题目描述:** 输入两个大写字母。求从第一个字母循环平移到第二个字母的最少步数;若向左和向右步数相同,输出 `BOTH`,否则输出 `LEFT` 或 `RIGHT`,并输出步数。 + +**输入格式:** 输入两个大写字母。 + +**输出格式:** 方向和最少步数。 + +**数据范围:** `A~Z`。 + +**输入样例:** +```text +A X +``` + +**输出样例:** +```text +LEFT 3 +``` + +**解题思路:** 右移步数为 `(b-a+26)%26`,左移步数为 `(a-b+26)%26`,比较两者。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char a, b; + cin >> a >> b; + int right = (b - a + 26) % 26; + int left = (a - b + 26) % 26; + if (left < right) cout << "LEFT " << left; + else if (right < left) cout << "RIGHT " << right; + else cout << "BOTH " << left; + return 0; +} +``` + +**易错提醒:** 两个字母相同也属于左右步数相同,此时输出 `BOTH 0`。 + +### 扩展7:解密未知平移量【略难】 + +**题目描述:** 某个大写字母经过向后循环平移变成另一个大写字母。输入原字母和结果字母,求 `0~25` 范围内唯一的平移量 `k`;再输入第三个大写字母,按同样规则平移并输出。 + +**输入格式:** 依次输入原字母、加密后的字母和需要继续加密的字母。 + +**输出格式:** 第一行平移量 `k`;第二行第三个字母平移后的结果。 + +**数据范围:** `A~Z`。 + +**输入样例:** +```text +Y B Z +``` + +**输出样例:** +```text +3 +C +``` + +**解题思路:** 先由前两个字母计算 `k=(encoded-original+26)%26`,再把 `k` 应用于第三个字母。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + char original, encoded, target; + cin >> original >> encoded >> target; + int k = (encoded - original + 26) % 26; + char answer = (target - 'A' + k) % 26 + 'A'; + cout << k << "\n" << answer; + return 0; +} +``` + +**易错提醒:** 计算平移量和应用平移量使用的是同一个循环公式。 diff --git a/第05课-嵌套循环与图形打印/教案-学生版.md b/第05课-嵌套循环与图形打印/教案-学生版.md new file mode 100644 index 0000000..d41d3fb --- /dev/null +++ b/第05课-嵌套循环与图形打印/教案-学生版.md @@ -0,0 +1,151 @@ +# 第5课:嵌套循环与图形打印 + +## 教学目标 +- 理解嵌套循环的执行流程(外层管行、内层管列) +- 能分析图形规律,写出对应的行列公式 +- 掌握矩形、直角三角形、等腰三角形、菱形的打印方法 +- 掌握多层循环以及分支与循环相互嵌套 + +## 核心考点:嵌套循环中的行列关系 + +### 考什么 +根据图形推导每一行的________数和________数,再使用嵌套循环输出。 + +### 必须理解 +________循环负责行数,________循环负责当前行中的字符数量。等腰三角形第 i 行有________个前导空格、________个星号。 + +### 核心写法 +```cpp +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= ________; j++) cout << ' '; + for (int j = 1; j <= ________; j++) cout << '*'; + ____________________; +} +``` + +### 常见错误 +- 换行应写在内层循环里面还是外面?________________ +- 为什么内层循环的次数常常与 i 有关?________________ +- 行号从0开始时,能否直接使用从1开始的公式?________ + +### 判断是否掌握 +- [ ] 我能列出每行空格数和符号数。 +- [ ] 我能由行号推导数量公式。 +- [ ] 我能正确安排内外层循环与换行。 + +--- + +## 课堂编程题 + +### 课堂1:实心与空心矩形【适中】 + +**题目描述:** 输入行数 `n`、列数 `m` 和模式 `type`。当 `type=1` 时打印实心矩形;当 `type=2` 时打印空心矩形。 + +**输入格式:** 依次输入行数、列数和图形模式。 + +**输出格式:** 用星号打印指定矩形。 + +**数据范围:** `1≤n,m≤30`,`type` 为 1 或 2。 + +**输入样例:** +```text +3 5 2 +``` + +**输出样例:** +```text +***** +* * +***** +``` + +**解题思路:** 双层循环枚举行列。实心模式总输出星号;空心模式只在首尾行或首尾列输出星号。 + +**易错提醒:** 每打印完一行才换行;`n=1` 或 `m=1` 时所有位置都在边界上。 + +### 课堂2:四种直角三角形【适中】 + +**题目描述:** 输入 `n` 和类型 `type`,打印边长为 `n` 的直角三角形:1 左下、2 右下、3 左上、4 右上。 + +**输入格式:** 输入三角形边长和图形类型。 + +**输出格式:** 指定方向的星号三角形。 + +**数据范围:** `1≤n≤30`,`1≤type≤4`。 + +**输入样例:** +```text +4 2 +``` + +**输出样例:** +```text + * + ** + *** +**** +``` + +**解题思路:** 第 `i` 行星号数量为 `i` 或 `n-i+1`;右对齐时先输出相应数量空格。 + +**易错提醒:** 右对齐不是改变星号数量,而是在星号前补空格。 + +### 课堂3:等腰三角形与菱形【适中】 + +**题目描述:** 输入 `n` 和模式 `type`。模式 1 打印高为 `n` 的实心等腰三角形;模式 2 打印上下高度均为 `n`、中间行不重复的菱形。 + +**输入格式:** 输入图形高度和图形模式。 + +**输出格式:** 指定图形。 + +**数据范围:** `1≤n≤20`。 + +**输入样例:** +```text +3 2 +``` + +**输出样例:** +```text + * + *** +***** + *** + * +``` + +**解题思路:** 上半部分第 `i` 行有 `n-i` 个空格和 `2i-1` 个星号;菱形下半部分按相反规律输出。 + +**易错提醒:** 菱形下半部分从 `n-1` 开始,避免中间最长行重复。 + +### 课堂4:九九乘法表【适中】 + +**题目描述:** 输入 `n`,打印 `1~n` 的下三角乘法表。第 `i` 行输出 `1×i` 到 `i×i`。 + +**输入格式:** 输入乘法表的最大因数。 + +**输出格式:** 下三角乘法表,每项格式为 `j*i=积`,项之间一个空格。 + +**数据范围:** `1≤n≤9`。 + +**输入样例:** +```text +3 +``` + +**输出样例:** +```text +1*1=1 +1*2=2 2*2=4 +1*3=3 2*3=6 3*3=9 +``` + +**解题思路:** 外层 `i` 控制行和第二个因数,内层 `j` 从 1 走到 `i`。 + +**易错提醒:** 内层上界是当前行号 `i`,不是固定的 `n`。 + +--- + +## 课后作业 + +> 请完成本课对应的课后作业,见编程题文件。 diff --git a/第05课-嵌套循环与图形打印/教案-教师版.md b/第05课-嵌套循环与图形打印/教案-教师版.md new file mode 100644 index 0000000..a209ce1 --- /dev/null +++ b/第05课-嵌套循环与图形打印/教案-教师版.md @@ -0,0 +1,310 @@ +# 第5课:嵌套循环与图形打印 + +## 教学目标 +- 理解嵌套循环的执行流程(外层管行、内层管列) +- 能分析图形规律,写出对应的行列公式 +- 掌握矩形、直角三角形、等腰三角形、菱形的打印方法 +- 掌握多层循环以及分支与循环相互嵌套的二级必考写法 + +## 核心考点:嵌套循环中的行列关系 + +### 考什么 +题目会给出矩形、三角形或菱形,要求分析每一行应输出多少空格和符号,再用嵌套循环打印。考试重点不是背某个图形,而是能从行号推导本行各部分的数量。 + +### 必须理解 +外层循环每执行一次就完成一整行,因此负责行数;内层循环在当前行中重复输出,负责列数或某种字符数量。解决图形题应先列出“第 i 行有多少空格、多少星号”,再把数量写成关于 i 和总行数 n 的公式。例如等腰三角形第 i 行:前导空格 `n-i` 个,星号 `2*i-1` 个。每一类字符分别使用一个内层循环,行末统一换行。 + +### 核心写法 +```cpp +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n - i; j++) cout << ' '; + for (int j = 1; j <= 2 * i - 1; j++) cout << '*'; + cout << endl; +} +``` +外层的 i 表示当前行,两个内层循环的上界由 i 决定,所以每行形状都会变化。 + +### 常见错误 +- 把换行写进内层循环,导致每输出一个字符就换行。 +- 内层循环上界始终写 n,无法随行号改变图形。 +- 行号从 0 开始,却直接套用从 1 开始推导的公式。 +- 只凭眼睛试代码,不先写出行号与数量表。 + +### 判断是否掌握 +面对新图形时,能先列前三行的空格数和符号数,推导出关于 i 的公式,再独立写出外层、内层和换行代码。 + +--- + +## 题目一:打印实心矩形 + +### 核心代码(一句话) +> 嵌套循环的核心:**外层循环管行数,内层循环管列数**,两层 `for` 一套就行。 + +### 自然语言思路 +我们要打印一个 n 行 m 列的矩形,全是星号。 + +第一步,确定要印几行:n 行,所以外层循环从 1 到 n。 +第二步,每一行里面要印几个星号:m 个,所以内层循环从 1 到 m。 +第三步,每印完一行,记得换行 `cout << endl`。 +第四步,把上面三步写成代码 —— 外层 `for` 套一个内层 `for`,里面 `cout` 星号,外面 `cout` 换行。 + +> 想象你在一行一行地写字:外层决定写几行,内层决定每行写几个字。 + +### 伪代码 +``` +1. 输入 n, m +2. for i = 1 到 n: // 外层:控制行 +3. for j = 1 到 m: // 内层:控制列 +4. 输出 "* " +5. 输出换行 +``` + +### 真实代码 +```cpp +#include +using namespace std; + +int main() { + int n, m; + cin >> n >> m; + + for (int i = 1; i <= n; i++) { // 外层循环:n 行 + for (int j = 1; j <= m; j++) { // 内层循环:每行 m 个 + cout << "* "; + } + cout << endl; // 每行结束换行 + } + return 0; +} +``` + +### 注意点 +1. **内层循环变量名**:习惯用 `j`,因为 `i` 已经被外层用了。不要在外层和内层用同一个变量名。 +2. **换行位置**:`cout << endl` 放在外层循环里面、内层循环外面,每行结束只换一次。 +3. **嵌套理解口诀**:外层每走一步,内层跑完全程。就像时钟——时针走一格,分针转一整圈。 + +--- + +## 题目二:直角三角形(左下 + 右下) + +### 核心代码(一句话) +> 直角三角形的核心:**第 i 行打印 i 个星号**,内层循环的终止条件用 `j <= i`。 + +### 自然语言思路 + +**左下直角三角形**: +第一步,观察规律:第1行1个星,第2行2个星,第3行3个星……第 i 行 i 个星。 +第二步,每行的星号数量**等于行号**。 +第三步,内层循环写成 `for (j = 1; j <= i; j++)`,就搞定了。 + +**右下直角三角形**: +第一步,先看空格:第1行前面有 n-1 个空格,第2行前面有 n-2 个空格……第 i 行前面有 n-i 个空格。 +第二步,空格用**另一个内层循环**先打印:`for (j = 1; j <= n - i; j++)`。 +第三步,再打印星号,和第 i 行 i 个星一样。 +第四步,每行顺序:先空格、后星号、再换行。 + +### 伪代码 + +**左下直角三角形**: +``` +1. 输入 n +2. for i = 1 到 n: +3. for j = 1 到 i: // 第 i 行 i 个星 +4. 输出 "* " +5. 输出换行 +``` + +**右下直角三角形**: +``` +1. 输入 n +2. for i = 1 到 n: +3. for j = 1 到 n-i: // 先打 n-i 个空格 +4. 输出 " " // 两个空格(跟星号对齐) +5. for j = 1 到 i: // 再打 i 个星 +6. 输出 "* " +7. 输出换行 +``` + +### 真实代码 + +**左下直角三角形**: +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= i; j++) { // 关键:j <= i + cout << "* "; + } + cout << endl; + } + return 0; +} +``` + +**右下直角三角形**: +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + for (int i = 1; i <= n; i++) { + // 先打空格 + for (int j = 1; j <= n - i; j++) { + cout << " "; // 两个空格,跟 "* " 宽度一致 + } + // 再打星号 + for (int j = 1; j <= i; j++) { + cout << "* "; + } + cout << endl; + } + return 0; +} +``` + +### 注意点 +1. **空格数量要对齐**:如果星号之间用 `"* "`(星号+空格),那么空格也要打印两个空格 `" "`,否则对不齐。 +2. **左下直角三角形最简单**:第 i 行 = i 个星,这是所有图形打印的母题,先把这个吃透。 +3. **右下 = 左下 + 空格**:右下直角就是把左下直角往右推,推的力度 = 前面的空格数。 + +--- + +## 题目三:等腰三角形与菱形 + +### 核心代码(一句话) +> 等腰三角形的核心:第 i 行有 **2i - 1** 个星号,前面的空格数为 **n - i**;菱形 = 正等腰 + 倒等腰。 + +### 自然语言思路 + +**等腰三角形**: +第一步,观察规律:n=5 时,第1行1个星,第2行3个星,第3行5个星……每行多2个星。 +第二步,归纳公式:第 i 行星号数 = `2 * i - 1`。 +第三步,空格规律和右下直角一样:第 i 行前面 n-i 个空格。 +第四步,先打空格,再打星号,换行,搞定。 + +**菱形**: +第一步,把菱形拆成两半:上半 = n 行等腰三角形,下半 = n-1 行倒等腰三角形。 +第二步,上半直接复用等腰三角形代码。 +第三步,下半倒过来:从第 n-1 行开始,递减到第 1 行。空格和星号公式不变,只是循环方向反过来。 +第四步,上下拼起来就是菱形。 + +### 伪代码 + +**等腰三角形**: +``` +1. 输入 n +2. for i = 1 到 n: +3. for j = 1 到 n-i: // 空格:n-i 个 +4. 输出 " " +5. for j = 1 到 2*i-1: // 星号:2i-1 个 +6. 输出 "*" +7. 输出换行 +``` + +**菱形**: +``` +1. 输入 n(上半部分行数) +2. // 上半:正等腰三角形 +3. for i = 1 到 n: +4. 打空格、打星号、换行(同上) +5. // 下半:倒等腰三角形 +6. for i = n-1 递减到 1: +7. 打空格、打星号、换行(同上) +``` + +### 真实代码 + +**等腰三角形**: +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + for (int i = 1; i <= n; i++) { + // 空格 + for (int j = 1; j <= n - i; j++) { + cout << " "; + } + // 星号:2i - 1 + for (int j = 1; j <= 2 * i - 1; j++) { + cout << "*"; + } + cout << endl; + } + return 0; +} +``` + +**菱形**: +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + // 上半部分:正等腰三角形 + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n - i; j++) cout << " "; + for (int j = 1; j <= 2 * i - 1; j++) cout << "*"; + cout << endl; + } + + // 下半部分:倒等腰三角形(n-1 行) + for (int i = n - 1; i >= 1; i--) { + for (int j = 1; j <= n - i; j++) cout << " "; + for (int j = 1; j <= 2 * i - 1; j++) cout << "*"; + cout << endl; + } + return 0; +} +``` + +### 注意点 +1. **等腰三角形星号公式 `2*i-1`**:这是最核心的公式,建议学生背下来。推导:第1行1个,每行多2个 → 等差数列 → 1 + 2×(i-1) = 2i-1。 +2. **菱形下半部分循环递减**:`for (i = n-1; i >= 1; i--)`,注意从 `n-1` 开始,不是 `n`,否则中间那行会重复。 +3. **空格只用一个空格**:等腰三角形通常用一个空格 `" "` 而不是两个,因为星号之间不加空格。 +4. **如果 n=1**:下半部分 `i = 0`,`0 >= 1` 为假,不执行,只打印一行星号,也是对的。 + +--- + +## 图形打印技巧总结 + +| 图形 | 第 i 行星号数 | 第 i 行空格数 | 关键点 | +|------|-------------|-------------|--------| +| 左下直角三角形 | `i` | 0 | 最简单,母题 | +| 左上直角三角形 | `n - i + 1` | 0 | 倒过来即可 | +| 右下直角三角形 | `i` | `n - i` | = 左下 + 空格 | +| 右上直角三角形 | `n - i + 1` | `i - 1` | = 左上 + 空格 | +| 等腰三角形 | `2i - 1` | `n - i` | 核心公式 | +| 倒等腰三角形 | `2i - 1` | `n - i` | i 递减即可 | +| 菱形 | 上同等腰,下同倒等腰 | 同上 | 上下拼接 | + +--- + +## 本课打油诗 + +> 外层循环管行数,内层循环管列数。 +> 图形打印找规律,行列关系是正路。 +> 第 i 行星号看公式,空格星号分清楚。 +> 等腰三角二 i 减一,菱形上下拼一處。 + +--- + +## 课后作业 +1. 输入 n,打印左上直角三角形(第1行 n 个星,第2行 n-1 个星……) +2. 输入 n,打印数字三角形(第1行1,第2行1 2……) +3. 输入 n 和 m,打印 n 行 m 列的空心矩形(只有边框有星号) +4. 输入 n,打印九九乘法表(第 i 行输出 i 个乘法式子) diff --git a/第05课-嵌套循环与图形打印/编程题-教师版.md b/第05课-嵌套循环与图形打印/编程题-教师版.md new file mode 100644 index 0000000..b086c55 --- /dev/null +++ b/第05课-嵌套循环与图形打印/编程题-教师版.md @@ -0,0 +1,660 @@ +# 第6课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | GESP等级 | 对应课堂题 | +|---|---|---| +| 外层控制行、内层控制列、实心矩形 | 二级必考 | 课堂1 | +| 行列关系、左下与右下三角形 | 二级必考 | 课堂2 | +| 空格数与星号数、等腰三角形与菱形 | 二级必考 | 课堂3 | +| 双层循环的非图形应用、行列组合 | 二级 | 课堂4 | + +## 一、课堂练习(4题) + +### 课堂1:实心与空心矩形【适中】 + +**题目描述:** 输入行数 `n`、列数 `m` 和模式 `type`。当 `type=1` 时打印实心矩形;当 `type=2` 时打印空心矩形。 + +**输入格式:** 依次输入行数、列数和图形模式。 + +**输出格式:** 用星号打印指定矩形。 + +**数据范围:** `1≤n,m≤30`,`type` 为 1 或 2。 + +**输入样例:** +```text +3 5 2 +``` + +**输出样例:** +```text +***** +* * +***** +``` + +**解题思路:** 双层循环枚举行列。实心模式总输出星号;空心模式只在首尾行或首尾列输出星号。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, m, type; + cin >> n >> m >> type; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (type == 1 || i == 1 || i == n || j == 1 || j == m) + cout << "*"; + else + cout << " "; + } + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 每打印完一行才换行;`n=1` 或 `m=1` 时所有位置都在边界上。 + +### 课堂2:四种直角三角形【适中】 + +**题目描述:** 输入 `n` 和类型 `type`,打印边长为 `n` 的直角三角形:1 左下、2 右下、3 左上、4 右上。 + +**输入格式:** 输入三角形边长和图形类型。 + +**输出格式:** 指定方向的星号三角形。 + +**数据范围:** `1≤n≤30`,`1≤type≤4`。 + +**输入样例:** +```text +4 2 +``` + +**输出样例:** +```text + * + ** + *** +**** +``` + +**解题思路:** 第 `i` 行星号数量为 `i` 或 `n-i+1`;右对齐时先输出相应数量空格。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, type; + cin >> n >> type; + for (int i = 1; i <= n; i++) { + int stars = (type == 1 || type == 2) ? i : n - i + 1; + int spaces = (type == 2 || type == 4) ? n - stars : 0; + for (int j = 1; j <= spaces; j++) cout << " "; + for (int j = 1; j <= stars; j++) cout << "*"; + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 右对齐不是改变星号数量,而是在星号前补空格。 + +### 课堂3:等腰三角形与菱形【适中】 + +**题目描述:** 输入 `n` 和模式 `type`。模式 1 打印高为 `n` 的实心等腰三角形;模式 2 打印上下高度均为 `n`、中间行不重复的菱形。 + +**输入格式:** 输入图形高度和图形模式。 + +**输出格式:** 指定图形。 + +**数据范围:** `1≤n≤20`。 + +**输入样例:** +```text +3 2 +``` + +**输出样例:** +```text + * + *** +***** + *** + * +``` + +**解题思路:** 上半部分第 `i` 行有 `n-i` 个空格和 `2i-1` 个星号;菱形下半部分按相反规律输出。 + +**参考代码:** +```cpp +#include +using namespace std; + +void printRow(int spaces, int stars) { + for (int j = 0; j < spaces; j++) cout << " "; + for (int j = 0; j < stars; j++) cout << "*"; + cout << "\n"; +} + +int main() { + int n, type; + cin >> n >> type; + for (int i = 1; i <= n; i++) printRow(n - i, 2 * i - 1); + if (type == 2) { + for (int i = n - 1; i >= 1; i--) printRow(n - i, 2 * i - 1); + } + return 0; +} +``` + +**易错提醒:** 菱形下半部分从 `n-1` 开始,避免中间最长行重复。 + +### 课堂4:九九乘法表【适中】 + +**题目描述:** 输入 `n`,打印 `1~n` 的下三角乘法表。第 `i` 行输出 `1×i` 到 `i×i`。 + +**输入格式:** 输入乘法表的最大因数。 + +**输出格式:** 下三角乘法表,每项格式为 `j*i=积`,项之间一个空格。 + +**数据范围:** `1≤n≤9`。 + +**输入样例:** +```text +3 +``` + +**输出样例:** +```text +1*1=1 +1*2=2 2*2=4 +1*3=3 2*3=6 3*3=9 +``` + +**解题思路:** 外层 `i` 控制行和第二个因数,内层 `j` 从 1 走到 `i`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= i; j++) { + if (j > 1) cout << " "; + cout << j << "*" << i << "=" << j * i; + } + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 内层上界是当前行号 `i`,不是固定的 `n`。 + +## 二、课后作业(3题) + +### 课后1:实心正方形【简单】 + +**题目描述:** 输入 `n`,打印 `n×n` 的星号正方形。 + +**输入格式:** 输入正方形的边长。 + +**输出格式:** 实心正方形。 + +**数据范围:** `1≤n≤20`。 + +**输入样例:** +```text +3 +``` + +**输出样例:** +```text +*** +*** +*** +``` + +**解题思路:** 外层循环 `n` 行,内层每行输出 `n` 个星号。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) cout << "*"; + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 换行语句应放在内层循环结束之后。 + +### 课后2:左下数字三角形【简单】 + +**题目描述:** 输入 `n`,第 `i` 行输出 `i` 个数字 `i`。 + +**输入格式:** 输入数字三角形的行数。 + +**输出格式:** 数字三角形。 + +**数据范围:** `1≤n≤9`。 + +**输入样例:** +```text +4 +``` + +**输出样例:** +```text +1 +22 +333 +4444 +``` + +**解题思路:** 外层行号决定输出数字,也决定内层循环次数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= i; j++) cout << i; + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 输出的是行号 `i`,不是列号 `j`。 + +### 课后3:棋盘格【简单】 + +**题目描述:** 输入 `n` 和 `m`,打印棋盘格;行列编号之和为偶数输出 `#`,否则输出 `.`。 + +**输入格式:** 输入棋盘格的行数和列数。 + +**输出格式:** `n` 行 `m` 列棋盘格。 + +**数据范围:** `1≤n,m≤30`。 + +**输入样例:** +```text +3 4 +``` + +**输出样例:** +```text +#.#. +.#.# +#.#. +``` + +**解题思路:** 双层循环枚举位置,根据 `(i+j)%2` 决定字符。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, m; + cin >> n >> m; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) + cout << ((i + j) % 2 == 0 ? '#' : '.'); + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 行列从 1 开始时,左上角的和为 2,应输出 `#`。 + +## 三、扩展练习(7题) + +### 扩展1:坐标表【基础综合】 + +**题目描述:** 输入 `n m`,按行输出所有坐标 `(i,j)`。 + +**输入格式:** 输入坐标表的行数和列数。 + +**输出格式:** 每行输出一行坐标,坐标之间一个空格。 + +**数据范围:** `1≤n,m≤10`。 + +**输入样例:** +```text +2 2 +``` + +**输出样例:** +```text +(1,1) (1,2) +(2,1) (2,2) +``` + +**解题思路:** 外层决定第一坐标,内层决定第二坐标。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, m; + cin >> n >> m; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + if (j > 1) cout << " "; + cout << "(" << i << "," << j << ")"; + } + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 同一行坐标之间输出空格,行末不必输出多余空格。 + +### 扩展2:回文数字三角形【中等】 + +**题目描述:** 输入 `n`,第 `i` 行依次输出 `1~i~1`。 + +**输入格式:** 输入回文数字三角形的行数。 + +**输出格式:** 回文数字三角形。 + +**数据范围:** `1≤n≤9`。 + +**输入样例:** +```text +4 +``` + +**输出样例:** +```text +1 +121 +12321 +1234321 +``` + +**解题思路:** 每行使用两个内层循环:第一个递增到 `i`,第二个从 `i-1` 递减到 1。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= i; j++) cout << j; + for (int j = i - 1; j >= 1; j--) cout << j; + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 下降部分从 `i-1` 开始,避免中间数字重复。 + +### 扩展3:空心直角三角形【中等】 + +**题目描述:** 输入 `n`,打印左下空心直角三角形。 + +**输入格式:** 输入空心直角三角形的边长。 + +**输出格式:** 指定图形。 + +**数据范围:** `2≤n≤30`。 + +**输入样例:** +```text +5 +``` + +**输出样例:** +```text +* +** +* * +* * +***** +``` + +**解题思路:** 第 `i` 行有 `i` 个位置;首列、斜边 `j=i`、底边 `i=n` 输出星号。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= i; j++) { + if (j == 1 || j == i || i == n) cout << "*"; + else cout << " "; + } + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 第二行两个位置都属于边界,所以会输出两个星号。 + +### 扩展4:字母方阵【中等】 + +**题目描述:** 输入 `n`,第 `i` 行第 `j` 列输出从 `A` 开始向后平移 `i+j-2` 位的字母,超过 `Z` 循环。 + +**输入格式:** 输入字母方阵的边长。 + +**输出格式:** `n×n` 字母方阵。 + +**数据范围:** `1≤n≤20`。 + +**输入样例:** +```text +3 +``` + +**输出样例:** +```text +ABC +BCD +CDE +``` + +**解题思路:** 用行列共同决定平移量,字符公式为 `'A'+(i+j-2)%26`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) + cout << (char)('A' + (i + j - 2) % 26); + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** ASCII 平移后要转换为字符;取模保证不会越过 `Z`。 + +### 扩展5:X形图案【中等】 + +**题目描述:** 输入奇数 `n`,打印 `n×n` 方阵,两条对角线输出 `*`,其他位置输出 `.`。 + +**输入格式:** 输入一个奇数,表示方阵的边长。 + +**输出格式:** X 形图案。 + +**数据范围:** `1≤n≤31`,`n` 为奇数。 + +**输入样例:** +```text +5 +``` + +**输出样例:** +```text +*...* +.*.*. +..*.. +.*.*. +*...* +``` + +**解题思路:** 主对角线满足 `i=j`,副对角线满足 `i+j=n+1`。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + if (i == j || i + j == n + 1) cout << "*"; + else cout << "."; + } + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 副对角线条件使用 `n+1`,因为行列编号从 1 开始。 + +### 扩展6:空心菱形【略难】 + +**题目描述:** 输入 `n`,打印总高度为 `2n-1` 的空心菱形。 + +**输入格式:** 输入菱形上半部分的高度。 + +**输出格式:** 空心菱形。 + +**数据范围:** `1≤n≤20`。 + +**输入样例:** +```text +3 +``` + +**输出样例:** +```text + * + * * +* * + * * + * +``` + +**解题思路:** 把每一行映射为到中间行的层数 `level`;输出前导空格、左边星号,宽度大于 1 时再输出内部空格和右边星号。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n; + cin >> n; + for (int row = 1; row <= 2 * n - 1; row++) { + int level = n - abs(n - row); + int outer = n - level; + int inner = 2 * level - 3; + for (int j = 0; j < outer; j++) cout << " "; + cout << "*"; + if (inner >= 0) { + for (int j = 0; j < inner; j++) cout << " "; + cout << "*"; + } + cout << "\n"; + } + return 0; +} +``` + +**易错提醒:** 顶点和底点只有一个星号,此时不能输出第二个星号。 + +### 扩展7:组合沙漏【略难】 + +**题目描述:** 输入 `n`,打印由星号组成的实心沙漏。第一行有 `2n-1` 个星号,逐行减少两个到 1,再逐行增加两个;中间行只输出一次。 + +**输入格式:** 输入沙漏上半部分的高度。 + +**输出格式:** 实心沙漏。 + +**数据范围:** `1≤n≤20`。 + +**输入样例:** +```text +3 +``` + +**输出样例:** +```text +***** + *** + * + *** +***** +``` + +**解题思路:** 上半部分第 `i` 行输出 `i-1` 个空格和 `2(n-i)+1` 个星号;下半部分反向恢复。 + +**参考代码:** +```cpp +#include +using namespace std; + +void printRow(int spaces, int stars) { + for (int j = 0; j < spaces; j++) cout << " "; + for (int j = 0; j < stars; j++) cout << "*"; + cout << "\n"; +} + +int main() { + int n; + cin >> n; + for (int i = 1; i <= n; i++) + printRow(i - 1, 2 * (n - i) + 1); + for (int i = n - 1; i >= 1; i--) + printRow(i - 1, 2 * (n - i) + 1); + return 0; +} +``` + +**易错提醒:** 下半部分从 `n-1` 开始,避免最窄的中间行重复。 diff --git a/第06课-字符数组与string/教案-学生版.md b/第06课-字符数组与string/教案-学生版.md new file mode 100644 index 0000000..841ac08 --- /dev/null +++ b/第06课-字符数组与string/教案-学生版.md @@ -0,0 +1,144 @@ +# 第8课:字符数组与string + +## 教学目标 +- 掌握 string 类的基本操作(长度、访问、截取、查找) +- 理解字符数组与 string 的区别 +- 能处理字符串遍历、逆序、回文判断等常见问题 +- 会用 `getline` 读取含空格的字符串 + +## 核心考点:字符串输入和逐字符处理 + +### 考什么 +正确读取单词或整行文字,再逐个访问________,完成统计、查找、替换或回文判断。 + +### 必须理解 +`cin >> s` 遇到________就停止;`getline(cin, s)` 会读取________。前面使用 `cin >>` 后,通常先执行________清理残留换行。字符串合法下标是________。 + +### 核心写法 +```cpp +cin >> n; +____________________; +string s; +____________________; + +for (int i = 0; i < ____________________; i++) { + char c = ________; +} +``` + +### 常见错误 +- 为什么 `cin >> s` 不能读完整句子?________________ +- `getline` 为什么有时会直接读到空行?________________ +- `i <= s.length()` 错在哪里?________________ + +### 判断是否掌握 +- [ ] 我能按输入格式选择读取方式。 +- [ ] 我能处理 `cin` 后的残留换行。 +- [ ] 我能无越界地逐字符处理字符串。 + +--- + +## 课堂编程题 + +### 课堂1:两种字符串输入【适中】 + +**题目描述:** 第一行输入一个不含空格的单词,使用字符数组保存;第二行输入一个可能含空格的句子,使用字符串保存。依次输出单词长度、句子长度以及句子中的空格数量。 + +**输入格式:** 第一行输入一个不含空格的单词;第二行输入一整句话。 + +**输出格式:** 依次输出单词长度、句子长度和空格数量。 + +**数据范围:** 单词和句子长度均不超过 200。 + +**输入样例:** +```text +GESP +I love C++ +``` + +**输出样例:** +```text +4 10 2 +``` + +**解题思路:** 字符数组以 `'\0'` 作为结束标志;读完单词后先忽略换行,再用 `getline` 读取整行字符串。 + +**易错提醒:** `cin` 不会读入空格;`getline` 前要处理输入缓冲区中的换行符。 + +### 课堂2:字符串分类统计【适中】 + +**题目描述:** 输入一整行字符串,统计大写字母、小写字母、数字、空格和其他字符的数量。 + +**输入格式:** 输入一整行字符串。 + +**输出格式:** 依次输出五类字符的数量。 + +**数据范围:** 字符串长度不超过 500。 + +**输入样例:** +```text +Hi 2026! +``` + +**输出样例:** +```text +1 1 4 1 1 +``` + +**解题思路:** 使用下标遍历字符串,通过互斥分支判断字符所属类别。 + +**易错提醒:** 字符串的合法下标为 0 到长度减 1;分类时只能让一个字符进入一个计数器。 + +### 课堂3:敏感词替换与截取【适中】 + +**题目描述:** 输入一个不含空格的原字符串、待查找字符串和替换字符串。将原字符串中第一次出现的待查找内容替换掉,并输出替换结果;若未找到,输出 `NOT FOUND`。找到时还要输出替换位置之前的前缀。 + +**输入格式:** 依次输入原字符串、待查找字符串和替换字符串,三者均不含空格。 + +**输出格式:** 找到时第一行输出替换后的字符串,第二行输出原字符串中匹配位置之前的部分;未找到时输出 `NOT FOUND`。 + +**数据范围:** 每个字符串长度不超过 200。 + +**输入样例:** +```text +abc123abc 123 XYZ +``` + +**输出样例:** +```text +abcXYZabc +abc +``` + +**解题思路:** 用 `find` 获得首次位置;用 `replace` 替换,用 `substr` 截取前缀。 + +**易错提醒:** 未找到时 `find` 返回 `string::npos`,不能直接拿这个位置去替换或截取。 + +### 课堂4:忽略格式的回文句【适中】 + +**题目描述:** 输入一整行字符串,忽略空格和英文字母大小写后,判断它是否为回文串。 + +**输入格式:** 输入一整行字符串。 + +**输出格式:** 是回文串输出 `YES`,否则输出 `NO`。 + +**数据范围:** 字符串长度不超过 500,只含英文字母和空格。 + +**输入样例:** +```text +Never odd or even +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 先构造一个无空格、全小写的新字符串,再用左右下标向中间比较。 + +**易错提醒:** 比较前必须统一大小写;空字符串和单字符都属于回文串。 + +--- + +## 课后作业 +> 请完成本课对应的课后作业,见编程题文件。 diff --git a/第06课-字符数组与string/教案-教师版.md b/第06课-字符数组与string/教案-教师版.md new file mode 100644 index 0000000..9d88a51 --- /dev/null +++ b/第06课-字符数组与string/教案-教师版.md @@ -0,0 +1,301 @@ +# 第8课:字符数组与string + +## 教学目标 +- 掌握 string 类的基本操作(长度、访问、截取、查找) +- 理解字符数组与 string 的区别 +- 能处理字符串遍历、逆序、回文判断等常见问题 +- 会用 `getline` 读取含空格的字符串 + +## 核心考点:字符串输入和逐字符处理 + +### 考什么 +常见题型包括读取一句话、统计各类字符、查找或替换子串、反转字符串和判断回文。首先要正确读入完整字符串,然后通过下标逐个处理字符。 + +### 必须理解 +`cin >> s` 遇到空格就停止,只适合读取一个不含空格的单词;`getline(cin, s)` 会读取整行。如果前面刚用 `cin >>` 读取数字,输入缓冲区可能残留换行符,应先用 `cin.ignore()` 清掉,否则 `getline` 会读到空行。字符串长度为 `s.length()`,合法下标仍是 `0~length-1`。逐字符处理时,每个 `s[i]` 都是 `char`,可配合 `isdigit`、`isalpha` 等函数分类。 + +### 核心写法 +```cpp +int n; +cin >> n; +cin.ignore(); +string s; +getline(cin, s); + +for (int i = 0; i < s.length(); i++) { + char c = s[i]; + // 对 c 进行判断、统计或修改 +} +``` +输入方式由数据格式决定;遍历上界由字符串的实际长度决定。 + +### 常见错误 +- 用 `cin >> s` 读取句子,只得到第一个单词。 +- 混用 `cin` 和 `getline` 时忘记处理残留换行。 +- 写成 `i <= s.length()`,访问字符串末尾之外的位置。 +- 把字符下标与子串位置混淆,或未检查 `find` 是否找到。 + +### 判断是否掌握 +能根据输入是否含空格选择 `cin` 或 `getline`,能解释空行问题,并能无越界地遍历字符串完成字符统计或修改。 + +--- + +## 题目一:字符串输入输出与字符统计 + +### 核心代码(一句话) +> 字符串处理的核心:**`getline(cin, s)` 读整行**,**`s.length()` 取长度**,**`s[i]` 访问每个字符**,配合字符判断函数做统计。 + +### 自然语言思路 +输入一句话,统计里面的大写字母、小写字母、数字、其他字符各有几个。 + +第一步,用 `getline(cin, s)` 读入整行(不能用 `cin >> s`,因为碰到空格就停了)。 +第二步,用 `s.length()` 或 `s.size()` 拿到字符串长度。 +第三步,用 `for` 循环遍历每个字符 `s[i]`。 +第四步,对每个字符用判断函数分类:`isupper()` 判断大写、`islower()` 判断小写、`isdigit()` 判断数字,剩下的就是"其他"。 +第五步,四个计数器分别累加,最后输出。 + +> 提示:`isupper`、`islower`、`isdigit` 这些函数在第1课学过,头文件是 ``。 + +### 伪代码 +``` +1. 输入一行字符串 s(用 getline) +2. upper = 0, lower = 0, digit = 0, other = 0 +3. for i = 0 到 s.length() - 1: +4. if isupper(s[i]): upper++ +5. else if islower(s[i]): lower++ +6. else if isdigit(s[i]): digit++ +7. else: other++ +8. 输出 upper, lower, digit, other +``` + +### 真实代码 +```cpp +#include +#include +#include // isupper, islower, isdigit +using namespace std; + +int main() { + string s; + getline(cin, s); // 读整行,含空格 + + int upper = 0, lower = 0, digit = 0, other = 0; + for (int i = 0; i < s.length(); i++) { + if (isupper(s[i])) { + upper++; + } else if (islower(s[i])) { + lower++; + } else if (isdigit(s[i])) { + digit++; + } else { + other++; + } + } + + cout << "大写:" << upper + << ",小写:" << lower + << ",数字:" << digit + << ",其他:" << other << endl; + return 0; +} +``` + +### 注意点 +1. **`cin >> s` vs `getline(cin, s)`**:`cin` 遇空格换行就停,`getline` 读整行(含空格)。如果前面用过 `cin`,需要用 `cin.ignore()` 吃掉残留的换行符。 +2. **`s.length()` vs `s.size()`**:两个完全一样,都可以用,没有区别。 +3. **访问越界**:下标范围 `0` 到 `s.length() - 1`,访问 `s[s.length()]` 会越界。 +4. **空格算"其他"**:上面的代码中空格既不是大写/小写/数字,会归入 other。如果想单独统计空格,加一个 `else if (s[i] == ' ')`。 + +--- + +## 题目二:字符串查找与替换 + +### 核心代码(一句话) +> 查找用 **`s.find("目标")`**,找不到返回 **`string::npos`**;替换用 **`s.replace(起始位置, 长度, "新内容")`**;截取用 **`s.substr(起始位置, 长度)`**。 + +### 自然语言思路 +在一句话里找到某个词的位置,然后把它替换成另一个词。 + +第一步,用 `s.find("目标词")` 查找,它会返回目标词第一次出现的位置(下标)。 +第二步,如果找到了(返回值 `!= string::npos`),用 `s.replace(位置, 长度, "新词")` 替换。 +第三步,如果找不到,`find` 返回 `string::npos`(一个很大的特殊值),直接提示"没找到"。 +第四步,`substr` 用来截取子串,比如 `s.substr(0, 5)` 表示从下标 0 开始取 5 个字符。 + +### 伪代码 +``` +1. 输入字符串 s +2. 输入要查找的词 target +3. pos = s.find(target) +4. if pos != string::npos: +5. 输入替换词 replacement +6. s.replace(pos, target.length(), replacement) +7. 输出 s +8. else: +9. 输出 "未找到" +``` + +### 真实代码 +```cpp +#include +#include +using namespace std; + +int main() { + string s; + getline(cin, s); + + string target; + cin >> target; + + int pos = s.find(target); + if (pos != string::npos) { + string replacement; + cin >> replacement; + s.replace(pos, target.length(), replacement); + cout << s << endl; + } else { + cout << "未找到" << endl; + } + return 0; +} +``` + +### 注意点 +1. **`string::npos` 是什么**:`npos` = "no position",是一个特殊常量,表示"没找到"。不要用 `-1` 或 `0` 来判断。 +2. **`replace` 会修改原字符串**:`s.replace(...)` 直接改了 `s`,如果还需要原来的值,先备份。 +3. **`substr` 参数**:`s.substr(开始下标, 长度)`。如果省略第二个参数,默认截取到末尾。 +4. **find 只找第一次出现**:如果想找所有出现的位置,需要循环 + 更新起始位置。 + +--- + +## 题目三:字符串逆序与回文判断 + +### 核心代码(一句话) +> 字符串逆序的核心:**从最后一个字符 `s.length()-1` 遍历到第一个字符 `0`**;回文判断的核心:**首尾字符两两比较,`s[i] != s[n-1-i]` 就不是回文**。 + +### 自然语言思路 + +**字符串逆序输出**: +第一步,拿到最后一个字符的下标:`s.length() - 1`。 +第二步,从后往前遍历:`for (i = s.length()-1; i >= 0; i--)`。 +第三步,逐个输出 `s[i]`,就得到了逆序字符串。 + +**判断回文**: +第一步,回文就是正着读和反着读一样,比如 "abcba"、"level"。 +第二步,只需要比较前一半和后一半:`s[0]` 比 `s[n-1]`,`s[1]` 比 `s[n-2]`……直到中间。 +第三步,用 `bool` 变量记录结果,默认 `true`。一旦发现不相等,改成 `false` 并 `break`。 +第四步,别忘了字符串可能有空格,但 GESP 题目一般用 `cin` 读单个词即可。 + +### 伪代码 + +**字符串逆序**: +``` +1. 输入 s +2. for i = s.length()-1 递减到 0: +3. 输出 s[i] +4. 输出换行 +``` + +**回文判断**: +``` +1. 输入 s +2. n = s.length() +3. isPalindrome = true +4. for i = 0 到 n/2 - 1: +5. if s[i] != s[n-1-i]: +6. isPalindrome = false +7. break +8. if isPalindrome: +9. 输出 "是回文" +10. else: +11. 输出 "不是回文" +``` + +### 真实代码 + +**字符串逆序**: +```cpp +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + + for (int i = s.length() - 1; i >= 0; i--) { + cout << s[i]; + } + cout << endl; + return 0; +} +``` + +**回文判断**: +```cpp +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + + int n = s.length(); + bool isPalindrome = true; + + for (int i = 0; i < n / 2; i++) { + if (s[i] != s[n - 1 - i]) { + isPalindrome = false; + break; + } + } + + if (isPalindrome) { + cout << "是回文" << endl; + } else { + cout << "不是回文" << endl; + } + return 0; +} +``` + +### 注意点 +1. **逆序遍历注意边界**:`for (i = s.length()-1; i >= 0; i--)`,不能忘了减 1,否则越界。 +2. **回文只需比一半**:循环 `n/2` 次就够了。如果 n=5,比下标 (0,4) 和 (1,3),中间的下标 2 不用比。 +3. **大小写问题**:严格判断回文时 "Aba" 不是回文(A ≠ a)。如果要忽略大小写,可以先用 `tolower`/`toupper` 统一转换。 +4. **string 可以直接用 `==` 比较**:`s1 == s2` 判断两个字符串是否完全相等,比字符数组方便得多。 + +--- + +## 字符数组 vs string 速查 + +| 特性 | 字符数组 `char[]` | string 类 | +|------|-------------------|-----------| +| 头文件 | 不需要(或 ``) | `` | +| 定义 | `char str[100];` | `string s;` | +| 输入 | `cin >> str;` | `cin >> s;` | +| 读整行 | `cin.getline(str, 100);` | `getline(cin, s);` | +| 长度 | `strlen(str)` | `s.length()` 或 `s.size()` | +| 拼接 | `strcat(str1, str2);` | `s1 + s2` | +| 比较 | `strcmp(str1, str2)` | `s1 == s2` | +| 结尾标志 | `'\0'` | 不需要关心 | + +> **建议**:GESP 考试中优先用 `string`,操作更简单,不容易出错。 + +--- + +## 本课打油诗 + +> string 类真好用,拼接查找样样通。 +> getline 读整行,length 长度记心中。 +> 下标访问像数组,逆序只需倒着走。 +> 回文首尾比一半,find 定位快如风。 + +--- + +## 课后作业 +1. 输入一个字符串,统计其中数字、字母、其他字符的个数 +2. 输入一个字符串,删除其中所有的空格后输出 +3. 输入两个字符串 A 和 B,判断 B 是否是 A 的子串 +4. 输入一个字符串,将其中的数字提取出来组成新字符串并输出 diff --git a/第06课-字符数组与string/编程题-教师版.md b/第06课-字符数组与string/编程题-教师版.md new file mode 100644 index 0000000..10c38df --- /dev/null +++ b/第06课-字符数组与string/编程题-教师版.md @@ -0,0 +1,655 @@ +# 第8课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | GESP等级 | 对应课堂题 | +|---|---|---| +| 字符数组、字符串结束标志、单词与整行输入 | 三级工具 | 课堂1 | +| `string` 长度、下标遍历、字符分类统计 | 三级工具 | 课堂2 | +| 字符串查找、替换与截取 | 三级工具 | 课堂3 | +| 字符串逆序与回文判断 | 三级工具 | 课堂4 | + +## 一、课堂练习(4题) + +### 课堂1:两种字符串输入【适中】 + +**题目描述:** 第一行输入一个不含空格的单词,使用字符数组保存;第二行输入一个可能含空格的句子,使用字符串保存。依次输出单词长度、句子长度以及句子中的空格数量。 + +**输入格式:** 第一行输入一个不含空格的单词;第二行输入一整句话。 + +**输出格式:** 依次输出单词长度、句子长度和空格数量。 + +**数据范围:** 单词和句子长度均不超过 200。 + +**输入样例:** +```text +GESP +I love C++ +``` + +**输出样例:** +```text +4 10 2 +``` + +**解题思路:** 字符数组以 `'\0'` 作为结束标志;读完单词后先忽略换行,再用 `getline` 读取整行字符串。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + char word[205]; + string sentence; + cin >> word; + cin.ignore(); + getline(cin, sentence); + + int wordLength = 0; + while (word[wordLength] != '\0') wordLength++; + + int spaces = 0; + for (char c : sentence) { + if (c == ' ') spaces++; + } + cout << wordLength << " " << sentence.size() << " " << spaces; + return 0; +} +``` + +**易错提醒:** `cin` 不会读入空格;`getline` 前要处理输入缓冲区中的换行符。 + +### 课堂2:字符串分类统计【适中】 + +**题目描述:** 输入一整行字符串,统计大写字母、小写字母、数字、空格和其他字符的数量。 + +**输入格式:** 输入一整行字符串。 + +**输出格式:** 依次输出五类字符的数量。 + +**数据范围:** 字符串长度不超过 500。 + +**输入样例:** +```text +Hi 2026! +``` + +**输出样例:** +```text +1 1 4 1 1 +``` + +**解题思路:** 使用下标遍历字符串,通过互斥分支判断字符所属类别。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + getline(cin, s); + int upper = 0, lower = 0, digit = 0, space = 0, other = 0; + for (int i = 0; i < (int)s.size(); i++) { + char c = s[i]; + if (c >= 'A' && c <= 'Z') upper++; + else if (c >= 'a' && c <= 'z') lower++; + else if (c >= '0' && c <= '9') digit++; + else if (c == ' ') space++; + else other++; + } + cout << upper << " " << lower << " " << digit << " " + << space << " " << other; + return 0; +} +``` + +**易错提醒:** 字符串的合法下标为 0 到长度减 1;分类时只能让一个字符进入一个计数器。 + +### 课堂3:敏感词替换与截取【适中】 + +**题目描述:** 输入一个不含空格的原字符串、待查找字符串和替换字符串。将原字符串中第一次出现的待查找内容替换掉,并输出替换结果;若未找到,输出 `NOT FOUND`。找到时还要输出替换位置之前的前缀。 + +**输入格式:** 依次输入原字符串、待查找字符串和替换字符串,三者均不含空格。 + +**输出格式:** 找到时第一行输出替换后的字符串,第二行输出原字符串中匹配位置之前的部分;未找到时输出 `NOT FOUND`。 + +**数据范围:** 每个字符串长度不超过 200。 + +**输入样例:** +```text +abc123abc 123 XYZ +``` + +**输出样例:** +```text +abcXYZabc +abc +``` + +**解题思路:** 用 `find` 获得首次位置;用 `replace` 替换,用 `substr` 截取前缀。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string source, target, replacement; + cin >> source >> target >> replacement; + size_t position = source.find(target); + if (position == string::npos) { + cout << "NOT FOUND"; + } else { + string prefix = source.substr(0, position); + source.replace(position, target.size(), replacement); + cout << source << "\n" << prefix; + } + return 0; +} +``` + +**易错提醒:** 未找到时 `find` 返回 `string::npos`,不能直接拿这个位置去替换或截取。 + +### 课堂4:忽略格式的回文句【适中】 + +**题目描述:** 输入一整行字符串,忽略空格和英文字母大小写后,判断它是否为回文串。 + +**输入格式:** 输入一整行字符串。 + +**输出格式:** 是回文串输出 `YES`,否则输出 `NO`。 + +**数据范围:** 字符串长度不超过 500,只含英文字母和空格。 + +**输入样例:** +```text +Never odd or even +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 先构造一个无空格、全小写的新字符串,再用左右下标向中间比较。 + +**参考代码:** +```cpp +#include +#include +#include +using namespace std; + +int main() { + string s, cleaned; + getline(cin, s); + for (char c : s) { + if (c != ' ') cleaned += (char)tolower(c); + } + bool palindrome = true; + int left = 0, right = (int)cleaned.size() - 1; + while (left < right) { + if (cleaned[left] != cleaned[right]) { + palindrome = false; + break; + } + left++; + right--; + } + cout << (palindrome ? "YES" : "NO"); + return 0; +} +``` + +**易错提醒:** 比较前必须统一大小写;空字符串和单字符都属于回文串。 + +## 二、课后作业(3题) + +### 课后1:字符串长度【简单】 + +**题目描述:** 输入一个不含空格的字符串,输出它的长度。 + +**输入格式:** 输入一个不含空格的字符串。 + +**输出格式:** 字符串长度。 + +**数据范围:** 字符串长度为 1~200。 + +**输入样例:** +```text +orange +``` + +**输出样例:** +```text +6 +``` + +**解题思路:** 使用字符串的 `size` 获取长度。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + cout << s.size(); + return 0; +} +``` + +**易错提醒:** 长度是字符个数,最后一个字符的下标是长度减 1。 + +### 课后2:统计指定字符【简单】 + +**题目描述:** 输入一个不含空格的字符串和一个字符,统计该字符出现次数。 + +**输入格式:** 输入一个字符串和一个待统计字符。 + +**输出格式:** 指定字符出现次数。 + +**数据范围:** 字符串长度不超过 200。 + +**输入样例:** +```text +banana a +``` + +**输出样例:** +```text +3 +``` + +**解题思路:** 遍历字符串,字符相等时计数。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + char target; + int count = 0; + cin >> s >> target; + for (char c : s) { + if (c == target) count++; + } + cout << count; + return 0; +} +``` + +**易错提醒:** 字符使用单引号概念,字符串使用双引号概念。 + +### 课后3:字符串逆序【简单】 + +**题目描述:** 输入一个不含空格的字符串,将它逆序输出。 + +**输入格式:** 输入一个不含空格的字符串。 + +**输出格式:** 逆序后的字符串。 + +**数据范围:** 字符串长度不超过 200。 + +**输入样例:** +```text +GESP +``` + +**输出样例:** +```text +PSEG +``` + +**解题思路:** 从最后一个下标向 0 倒序输出。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + for (int i = (int)s.size() - 1; i >= 0; i--) cout << s[i]; + return 0; +} +``` + +**易错提醒:** 倒序循环变量应使用有符号整数,否则减到 0 以下时可能出错。 + +## 三、扩展练习(7题) + +### 扩展1:大小写交替转换【基础综合】 + +**题目描述:** 输入一个不含空格的字符串,把大写字母转成小写、小写字母转成大写,其他字符保持不变。 + +**输入格式:** 输入一个不含空格的字符串。 + +**输出格式:** 转换后的字符串。 + +**数据范围:** 字符串长度不超过 200。 + +**输入样例:** +```text +AbC123xY +``` + +**输出样例:** +```text +aBc123Xy +``` + +**解题思路:** 遍历并按 ASCII 范围判断,分别调用大小写转换函数。 + +**参考代码:** +```cpp +#include +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + for (char &c : s) { + if (c >= 'A' && c <= 'Z') c = (char)tolower(c); + else if (c >= 'a' && c <= 'z') c = (char)toupper(c); + } + cout << s; + return 0; +} +``` + +**易错提醒:** 数字和符号不要改变。 + +### 扩展2:删除指定字符【基础综合】 + +**题目描述:** 输入一整行字符串和一个字符,删除字符串中所有该字符后输出结果。 + +**输入格式:** 第一行输入一整行字符串;第二行输入待删除字符。 + +**输出格式:** 删除后的字符串。 + +**数据范围:** 原字符串长度不超过 500。 + +**输入样例:** +```text +a cat and a hat +a +``` + +**输出样例:** +```text + ct nd ht +``` + +**解题思路:** 构造新字符串,只加入不等于目标的字符。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s, answer; + char target; + getline(cin, s); + cin >> target; + for (char c : s) { + if (c != target) answer += c; + } + cout << answer; + return 0; +} +``` + +**易错提醒:** 删除字符后空格仍需保留。 + +### 扩展3:单词数量【中等】 + +**题目描述:** 输入一整行英文句子,单词之间可能有多个空格,统计单词数量。 + +**输入格式:** 输入一整行英文句子。 + +**输出格式:** 单词数量。 + +**数据范围:** 句子长度不超过 500,只含英文字母和空格。 + +**输入样例:** +```text + I love Cpp +``` + +**输出样例:** +```text +3 +``` + +**解题思路:** 当当前位置是字母,且它是首字符或前一个字符为空格时,说明一个新单词开始。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + getline(cin, s); + int count = 0; + for (int i = 0; i < (int)s.size(); i++) { + if (s[i] != ' ' && (i == 0 || s[i - 1] == ' ')) count++; + } + cout << count; + return 0; +} +``` + +**易错提醒:** 不能用空格数加一,因为句首、句尾和连续空格都会造成错误。 + +### 扩展4:查找所有位置【中等】 + +**题目描述:** 输入原字符串和一个字符,输出该字符出现的所有位置,位置从 1 开始;若没有出现,输出 `NONE`。 + +**输入格式:** 输入一个不含空格的字符串和一个待查找字符。 + +**输出格式:** 所有出现位置或 `NONE`。 + +**数据范围:** 字符串长度不超过 500。 + +**输入样例:** +```text +mississippi s +``` + +**输出样例:** +```text +3 4 6 7 +``` + +**解题思路:** 完整遍历而不提前结束,每次匹配都输出当前位置加一。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + char target; + cin >> s >> target; + int count = 0; + for (int i = 0; i < (int)s.size(); i++) { + if (s[i] == target) { + if (count) cout << " "; + cout << i + 1; + count++; + } + } + if (count == 0) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 题目要求所有位置,不能找到第一个后使用 `break`。 + +### 扩展5:连续字符压缩【中等】 + +**题目描述:** 输入一个仅含英文字母的字符串,将连续相同字符压缩为“字符加次数”。即使只出现一次,也要输出次数。 + +**输入格式:** 输入一个仅含英文字母且不含空格的字符串。 + +**输出格式:** 压缩后的字符串。 + +**数据范围:** 字符串长度为 1~500。 + +**输入样例:** +```text +aaabbcaaaa +``` + +**输出样例:** +```text +a3b2c1a4 +``` + +**解题思路:** 记录当前连续段的字符和长度;遇到不同字符时输出上一段,最后补输出末段。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + char current = s[0]; + int count = 1; + for (int i = 1; i < (int)s.size(); i++) { + if (s[i] == current) { + count++; + } else { + cout << current << count; + current = s[i]; + count = 1; + } + } + cout << current << count; + return 0; +} +``` + +**易错提醒:** 循环结束后最后一段尚未输出,必须单独处理。 + +### 扩展6:最长回文前缀【略难】 + +**题目描述:** 输入一个不含空格的字符串,找出从第一个字符开始的最长回文子串并输出。 + +**输入格式:** 输入一个不含空格的字符串。 + +**输出格式:** 最长回文前缀。 + +**数据范围:** 字符串长度为 1~200。 + +**输入样例:** +```text +abacdf +``` + +**输出样例:** +```text +aba +``` + +**解题思路:** 从整个字符串长度开始递减,检查前缀两端是否对称;第一个成功的就是最长答案。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string s; + cin >> s; + for (int length = (int)s.size(); length >= 1; length--) { + bool palindrome = true; + for (int left = 0, right = length - 1; left < right; + left++, right--) { + if (s[left] != s[right]) { + palindrome = false; + break; + } + } + if (palindrome) { + cout << s.substr(0, length); + break; + } + } + return 0; +} +``` + +**易错提醒:** 检查的是从下标 0 开始的前缀,不是任意位置的回文子串。 + +### 扩展7:两串是否由相同字母组成【略难】 + +**题目描述:** 输入两个只含小写字母的字符串,判断它们是否包含完全相同的字母及出现次数,字符顺序可以不同。 + +**输入格式:** 输入两个只含小写字母且不含空格的字符串。 + +**输出格式:** 满足条件输出 `YES`,否则输出 `NO`。 + +**数据范围:** 每个字符串长度不超过 500。 + +**输入样例:** +```text +listen silent +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 使用长度为 26 的计数数组;遍历第一个字符串时加一,遍历第二个时减一,最后全部为零才匹配。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + string a, b; + cin >> a >> b; + int count[26] = {0}; + for (char c : a) count[c - 'a']++; + for (char c : b) count[c - 'a']--; + bool same = true; + for (int i = 0; i < 26; i++) { + if (count[i] != 0) { + same = false; + break; + } + } + cout << (same ? "YES" : "NO"); + return 0; +} +``` + +**易错提醒:** 只比较字符串长度不够,必须比较每个字母的出现次数。 diff --git a/第07课-数组计数法/教案-学生版.md b/第07课-数组计数法/教案-学生版.md new file mode 100644 index 0000000..a9c8c5d --- /dev/null +++ b/第07课-数组计数法/教案-学生版.md @@ -0,0 +1,171 @@ +# 第9课:数组计数法(学生版) + +## 教学目标 + +- 理解“数值作下标、次数存数组”的计数思想 +- 掌握数组计数的核心写法 +- 能用计数数组完成频次统计、最高频查找和去重升序输出 +- 知道数组计数法的适用条件和下标范围限制 + +## 核心考点:数值映射为下标的数组计数思想 + +### 考什么 +统计整数的出现次数,寻找最高频数字,或完成________和有序输出。 + +### 必须理解 +用数组下标 x 表示________,用 `cnt[x]` 表示________。每读到一次 x,就执行________。数组大小必须覆盖题目给出的全部________。 + +### 核心写法 +```cpp +int cnt[________] = {}; +for (int i = 0; i < n; i++) { + int x; + cin >> x; + ____________________; +} +for (int i = 0; i <= 100; i++) { + if (____________________) cout << i; +} +``` + +### 常见错误 +- 范围是0~100时,数组为什么要开101?________________ +- 计数数组为什么必须清零?________________ +- 负数能否直接作为数组下标?________ + +### 判断是否掌握 +- [ ] 我能根据数据范围确定数组大小。 +- [ ] 我能解释下标与计数值分别代表什么。 +- [ ] 我能用计数数组完成频次或去重任务。 + +--- + +## 课前认识:什么是数组计数法 + +如果输入的数字都在 `0~100` 之间,可以准备一个 `cnt[101]` 数组。 + +- 数字 `x` 对应数组下标:________ +- `cnt[x]` 保存:________________ +- 每读到一个数字 x,执行:________________ + +--- + +## 课堂编程题 + +### 课堂1:成绩频次表【适中】 + +**题目描述:** 输入若干名学生的成绩,按分数从小到大输出每个出现过的分数及人数。 + +**输入格式:** 第一行输入学生人数;第二行输入每名学生的成绩。 + +**输出格式:** 每行输出一个出现过的分数和对应人数。 + +**数据范围:** 学生人数为 1~1000,成绩为 0~100。 + +**输入样例:** +```text +6 +80 90 80 75 90 80 +``` + +**输出样例:** +```text +75 1 +80 3 +90 2 +``` + +**解题思路:** 分数可直接作为下标;每读到一个分数,对应计数加一,最后从 0 遍历到 100。 + +**易错提醒:** 允许分数 100,所以数组至少需要 101 个位置。 + +--- + +### 课堂2:最受欢迎的号码【适中】 + +**题目描述:** 输入若干个 1~50 的号码,输出出现次数最多的号码及次数;若并列,输出较小号码。 + +**输入格式:** 第一行输入号码总数;第二行输入所有号码。 + +**输出格式:** 输出最受欢迎的号码及其出现次数。 + +**数据范围:** 号码总数为 1~1000。 + +**输入样例:** +```text +7 +8 3 8 5 3 8 3 +``` + +**输出样例:** +```text +3 3 +``` + +**解题思路:** 先计数,再从小到大打擂台;只有次数严格更多才更新,便能保留并列时较小号码。 + +**易错提醒:** 更新条件不能写大于等于,否则并列时会留下较大号码。 + +--- + +### 课堂3:去重并升序输出【适中】 + +**题目描述:** 输入若干个 0~100 的整数,去掉重复数字并升序输出,同时输出不同数字的个数。 + +**输入格式:** 第一行输入整数数量;第二行输入所有整数。 + +**输出格式:** 第一行输出不同数字的个数;第二行升序输出不同数字。 + +**数据范围:** 整数数量为 1~1000。 + +**输入样例:** +```text +8 +4 2 4 1 2 9 1 5 +``` + +**输出样例:** +```text +5 +1 2 4 5 9 +``` + +**解题思路:** 计数后从小到大遍历下标,次数大于 0 就输出一次并增加不同数字个数。 + +**易错提醒:** 下标从小到大遍历时,答案天然有序。 + +--- + +### 课堂4:温度统计【适中】 + +**题目描述:** 输入若干个 -20~50 的整数温度,输出每个出现过的温度及次数,并输出出现次数最多的温度;并列取较低温度。 + +**输入格式:** 第一行输入记录数量;第二行输入所有温度。 + +**输出格式:** 先按温度从低到高输出频次表,最后一行输出最高频温度。 + +**数据范围:** 记录数量为 1~1000。 + +**输入样例:** +```text +6 +-2 0 -2 3 0 -2 +``` + +**输出样例:** +```text +-2 3 +0 2 +3 1 +BEST -2 +``` + +**解题思路:** 温度加 20 后映射到 0~70;输出和比较时再把下标减 20 还原。 + +**易错提醒:** 负数不能直接作为数组下标;映射和还原必须使用相同偏移量。 + +--- + +## 课后作业 + +> 请完成本课对应的课后作业,见编程题文件。 diff --git a/第07课-数组计数法/教案-教师版.md b/第07课-数组计数法/教案-教师版.md new file mode 100644 index 0000000..b6656b9 --- /dev/null +++ b/第07课-数组计数法/教案-教师版.md @@ -0,0 +1,283 @@ +# 第9课:数组计数法 + +## 教学目标 + +- 理解“数值作下标、次数存数组”的计数思想 +- 掌握数组计数的核心写法 `cnt[x]++` +- 能用计数数组完成频次统计、最高频查找和去重升序输出 +- 知道数组计数法的适用条件和下标范围限制 + +## 核心考点:数值映射为下标的数组计数思想 + +### 考什么 +题目会给出一组范围明确的整数,要求统计频次、找出现次数最多的数、判断某数是否出现,或去重后按序输出。关键是不用两两比较,而是让每个数直接找到自己的计数位置。 + +### 必须理解 +当数值 x 能作为合法下标时,用 `cnt[x]` 表示 x 出现的次数。每读到一次 x,就执行 `cnt[x]++`。统计结束后,遍历计数数组即可获得所有频次;因为下标从小到大遍历,输出天然有序。使用前必须根据数据范围确定数组大小并清零。若存在负数,可在范围较小时整体偏移;若数值范围极大,就不适合直接开计数数组。 + +### 核心写法 +```cpp +int cnt[101] = {}; +for (int i = 0; i < n; i++) { + int x; + cin >> x; + cnt[x]++; +} +for (int value = 0; value <= 100; value++) { + if (cnt[value] > 0) { + cout << value << ' ' << cnt[value] << endl; + } +} +``` +这里下标 `value` 代表具体数值,数组元素 `cnt[value]` 代表次数,两者含义不能混淆。 + +### 常见错误 +- 数值范围到100,却只开 `cnt[100]`,无法访问下标100。 +- 计数数组未清零,初始垃圾值会参与累加。 +- 输入可能为负数或超范围,仍直接作为下标导致越界。 +- 输出最高频数字时混淆“数字本身”和“出现次数”。 + +### 判断是否掌握 +能根据数据范围确定计数数组大小,能解释 `cnt[x]++` 的含义,并能通过一次计数、一次遍历完成频次查询、众数或去重输出。 + +--- + +## 课前认识:什么是数组计数法 + +如果输入的数字都在 `0~100` 之间,可以准备一个 `cnt[101]` 数组: + +- 数字 `x` 出现一次,就执行 `cnt[x]++` +- `cnt[3]` 表示数字 3 出现了几次 +- `cnt[100]` 表示数字 100 出现了几次 + +例如输入 `3 1 3 2 1 3`,计数后有: + +| 数字(下标) | 0 | 1 | 2 | 3 | +|---|---:|---:|---:|---:| +| 次数(cnt) | 0 | 2 | 1 | 3 | + +> 数组计数法的核心,就是把数字直接放到对应编号的“小格子”里做记号。 + +--- + +## 题目一:统计数字出现次数 + +### 题目描述 + +输入 n 个 `0~100` 之间的整数,按数字从小到大的顺序,输出每个出现过的数字及其出现次数。 + +### 核心代码(一句话) + +> 每读到一个数字 `x`,就让对应位置加一:`cnt[x]++`。 + +### 自然语言思路 + +第一步,准备 `cnt[101]`,把每个位置都初始化为 0。 + +第二步,循环读入 n 个数字。读到 `x`,就在编号为 x 的格子里加一次,也就是 `cnt[x]++`。 + +第三步,从 0 遍历到 100。若 `cnt[i] > 0`,说明数字 i 出现过,输出 i 和它的次数。 + +### 伪代码 + +```text +1. 输入 n +2. 创建并清零计数数组 cnt[101] +3. 重复 n 次: +4. 输入 x +5. cnt[x] 加 1 +6. 从 i = 0 遍历到 100: +7. 如果 cnt[i] > 0: +8. 输出 i 和 cnt[i] +``` + +### 真实代码 + +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + int cnt[101] = {}; + for (int i = 0; i < n; i++) { + int x; + cin >> x; + cnt[x]++; + } + + for (int i = 0; i <= 100; i++) { + if (cnt[i] > 0) { + cout << i << ":" << cnt[i] << endl; + } + } + return 0; +} +``` + +### 注意点 + +- `int cnt[101] = {};` 会把整个数组清零。 +- 数值范围是 `0~100`,所以数组需要 101 个位置。 +- 输出时只输出 `cnt[i] > 0` 的数字。 +- 输入值不能超出数组下标范围,否则会越界。 + +--- + +## 题目二:寻找出现次数最多的数字 + +### 题目描述 + +输入 n 个 `0~100` 之间的整数,输出出现次数最多的数字。如果多个数字出现次数相同,输出其中较小的数字。 + +### 核心代码(一句话) + +> 先用 `cnt[x]++` 统计次数,再从小到大打擂台;只有次数严格更多时才更换答案。 + +### 自然语言思路 + +第一步,先像题目一一样统计每个数字出现的次数。 + +第二步,假设数字 0 暂时是擂主,用 `bestValue` 保存擂主数字,用 `bestCount` 保存它的出现次数。 + +第三步,从数字 1 遍历到 100。如果 `cnt[i] > bestCount`,说明 i 出现得更多,让 i 成为新擂主。 + +第四步,如果次数相同,不更换擂主。因为我们从小到大遍历,先成为擂主的数字更小,正好满足并列时取较小值。 + +### 伪代码 + +```text +1. 输入 n,统计 cnt +2. bestValue = 0 +3. bestCount = cnt[0] +4. 从 i = 1 遍历到 100: +5. 如果 cnt[i] > bestCount: +6. bestValue = i +7. bestCount = cnt[i] +8. 输出 bestValue +``` + +### 真实代码 + +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + int cnt[101] = {}; + for (int i = 0; i < n; i++) { + int x; + cin >> x; + cnt[x]++; + } + + int bestValue = 0; + int bestCount = cnt[0]; + for (int i = 1; i <= 100; i++) { + if (cnt[i] > bestCount) { + bestValue = i; + bestCount = cnt[i]; + } + } + + cout << bestValue << endl; + return 0; +} +``` + +### 注意点 + +- 题目保证 `n >= 1`,因此一定存在答案。 +- 判断条件必须是 `>`,不能写成 `>=`;否则并列时会留下较大的数字。 +- `bestValue` 保存数字,`bestCount` 保存次数,不要混淆。 +- 这道题把第4课的“打擂台”和本课的数组计数结合了起来。 + +--- + +## 题目三:去重后升序输出 + +### 题目描述 + +输入 n 个 `0~100` 之间的整数,去掉重复数字,并按从小到大的顺序输出。 + +### 核心代码(一句话) + +> 计数后从小到大遍历下标,`cnt[i] > 0` 就输出 i,每个数字只输出一次。 + +### 自然语言思路 + +第一步,用计数数组记录每个数字是否出现过。 + +第二步,从 0 遍历到 100。数组下标本身就是数字,而且遍历顺序天然是从小到大。 + +第三步,只要 `cnt[i] > 0`,就输出一次 i。不管它原来出现两次还是十次,都只输出一次,因此完成去重。 + +### 伪代码 + +```text +1. 输入 n,统计 cnt +2. 从 i = 0 遍历到 100: +3. 如果 cnt[i] > 0: +4. 输出 i +``` + +### 真实代码 + +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + int cnt[101] = {}; + for (int i = 0; i < n; i++) { + int x; + cin >> x; + cnt[x]++; + } + + bool first = true; + for (int i = 0; i <= 100; i++) { + if (cnt[i] > 0) { + if (!first) cout << ' '; + cout << i; + first = false; + } + } + cout << endl; + return 0; +} +``` + +### 注意点 + +- 遍历计数数组时,下标本身按从小到大变化,因此输出天然有序。 +- 去重只关心“是否出现”,所以判断 `cnt[i] > 0` 即可。 +- `first` 用来控制空格,避免行首或行尾出现多余空格。 +- 如果数字范围非常大,例如到 `10^9`,就不适合直接开这么大的计数数组。 + +--- + +## 本课打油诗 + +> 数值下标来对号, +> 每次出现次数高。 +> 从小到大逐格找, +> 统计去重一招好。 + +--- + +## 课后作业 + +1. 输入 n 个 `0~50` 的整数,再输入一个数字 q,输出 q 出现了多少次。 +2. 输入 n 个 `0~100` 的整数,输出出现次数最少且出现过的数字;并列时输出较小者。 +3. 输入 n 个学生的分数(`0~100`),依次输出每个分数段的人数:不及格、60~79、80~89、90~100。 + +> 提示:先判断题目中的数字范围,再决定计数数组要开多大。 diff --git a/第07课-数组计数法/编程题-教师版.md b/第07课-数组计数法/编程题-教师版.md new file mode 100644 index 0000000..b845ff6 --- /dev/null +++ b/第07课-数组计数法/编程题-教师版.md @@ -0,0 +1,721 @@ +# 第9课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | 等级定位 | 对应课堂题 | +|---|---|---| +| 数值映射下标、计数数组初始化与边界 | 三级工具,服务一二级统计 | 课堂1 | +| 最高频打擂台与并列规则 | 三级工具,服务分支判断 | 课堂2 | +| 去重与按下标顺序输出 | 三级工具,服务循环遍历 | 课堂3 | +| 负数偏移映射与适用范围判断 | 三级工具,服务类型转换 | 课堂4 | + +## 一、课堂练习(4题) + +### 课堂1:成绩频次表【适中】 + +**题目描述:** 输入若干名学生的成绩,按分数从小到大输出每个出现过的分数及人数。 + +**输入格式:** 第一行输入学生人数;第二行输入每名学生的成绩。 + +**输出格式:** 每行输出一个出现过的分数和对应人数。 + +**数据范围:** 学生人数为 1~1000,成绩为 0~100。 + +**输入样例:** +```text +6 +80 90 80 75 90 80 +``` + +**输出样例:** +```text +75 1 +80 3 +90 2 +``` + +**解题思路:** 分数可直接作为下标;每读到一个分数,对应计数加一,最后从 0 遍历到 100。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[101] = {0}; + cin >> n; + for (int i = 0; i < n; i++) { + int score; + cin >> score; + count[score]++; + } + for (int score = 0; score <= 100; score++) { + if (count[score] > 0) + cout << score << " " << count[score] << "\n"; + } + return 0; +} +``` + +**易错提醒:** 允许分数 100,所以数组至少需要 101 个位置。 + +### 课堂2:最受欢迎的号码【适中】 + +**题目描述:** 输入若干个 1~50 的号码,输出出现次数最多的号码及次数;若并列,输出较小号码。 + +**输入格式:** 第一行输入号码总数;第二行输入所有号码。 + +**输出格式:** 输出最受欢迎的号码及其出现次数。 + +**数据范围:** 号码总数为 1~1000。 + +**输入样例:** +```text +7 +8 3 8 5 3 8 3 +``` + +**输出样例:** +```text +3 3 +``` + +**解题思路:** 先计数,再从小到大打擂台;只有次数严格更多才更新,便能保留并列时较小号码。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[51] = {0}; + cin >> n; + for (int i = 0; i < n; i++) { + int number; + cin >> number; + count[number]++; + } + int bestNumber = 1; + for (int number = 2; number <= 50; number++) { + if (count[number] > count[bestNumber]) + bestNumber = number; + } + cout << bestNumber << " " << count[bestNumber]; + return 0; +} +``` + +**易错提醒:** 更新条件不能写大于等于,否则并列时会留下较大号码。 + +### 课堂3:去重并升序输出【适中】 + +**题目描述:** 输入若干个 0~100 的整数,去掉重复数字并升序输出,同时输出不同数字的个数。 + +**输入格式:** 第一行输入整数数量;第二行输入所有整数。 + +**输出格式:** 第一行输出不同数字的个数;第二行升序输出不同数字。 + +**数据范围:** 整数数量为 1~1000。 + +**输入样例:** +```text +8 +4 2 4 1 2 9 1 5 +``` + +**输出样例:** +```text +5 +1 2 4 5 9 +``` + +**解题思路:** 计数后从小到大遍历下标,次数大于 0 就输出一次并增加不同数字个数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[101] = {0}, kinds = 0; + cin >> n; + for (int i = 0; i < n; i++) { + int number; + cin >> number; + count[number]++; + } + for (int number = 0; number <= 100; number++) + if (count[number] > 0) kinds++; + cout << kinds << "\n"; + bool first = true; + for (int number = 0; number <= 100; number++) { + if (count[number] > 0) { + if (!first) cout << " "; + cout << number; + first = false; + } + } + return 0; +} +``` + +**易错提醒:** 下标从小到大遍历时,答案天然有序。 + +### 课堂4:温度统计【适中】 + +**题目描述:** 输入若干个 -20~50 的整数温度,输出每个出现过的温度及次数,并输出出现次数最多的温度;并列取较低温度。 + +**输入格式:** 第一行输入记录数量;第二行输入所有温度。 + +**输出格式:** 先按温度从低到高输出频次表,最后一行输出最高频温度。 + +**数据范围:** 记录数量为 1~1000。 + +**输入样例:** +```text +6 +-2 0 -2 3 0 -2 +``` + +**输出样例:** +```text +-2 3 +0 2 +3 1 +BEST -2 +``` + +**解题思路:** 温度加 20 后映射到 0~70;输出和比较时再把下标减 20 还原。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[71] = {0}; + cin >> n; + for (int i = 0; i < n; i++) { + int temperature; + cin >> temperature; + count[temperature + 20]++; + } + int best = 0; + for (int index = 0; index <= 70; index++) { + if (count[index] > 0) + cout << index - 20 << " " << count[index] << "\n"; + if (count[index] > count[best]) best = index; + } + cout << "BEST " << best - 20; + return 0; +} +``` + +**易错提醒:** 负数不能直接作为数组下标;映射和还原必须使用相同偏移量。 + +## 二、课后作业(3题) + +### 课后1:查询出现次数【简单】 + +**题目描述:** 输入若干个 0~50 的整数和一个查询数字,输出该数字出现次数。 + +**输入格式:** 第一行输入整数数量;第二行输入所有整数;第三行输入查询数字。 + +**输出格式:** 查询数字的出现次数。 + +**数据范围:** 整数数量为 1~1000。 + +**输入样例:** +```text +5 +1 3 3 8 3 +3 +``` + +**输出样例:** +```text +3 +``` + +**解题思路:** 使用计数数组统计后,直接访问查询数字对应的位置。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[51] = {0}, query; + cin >> n; + for (int i = 0; i < n; i++) { + int number; + cin >> number; + count[number]++; + } + cin >> query; + cout << count[query]; + return 0; +} +``` + +**易错提醒:** 数字本身是下标,数组中保存的是次数。 + +### 课后2:最少出现的数字【简单】 + +**题目描述:** 输入若干个 0~100 的整数,输出出现次数最少且确实出现过的数字;并列取较小者。 + +**输入格式:** 第一行输入整数数量;第二行输入所有整数。 + +**输出格式:** 满足条件的数字和出现次数。 + +**数据范围:** 整数数量为 1~1000。 + +**输入样例:** +```text +6 +2 2 3 4 4 4 +``` + +**输出样例:** +```text +3 1 +``` + +**解题思路:** 遍历时跳过次数为 0 的数字,再用打擂台寻找最小正次数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[101] = {0}, answer = -1; + cin >> n; + for (int i = 0; i < n; i++) { + int number; + cin >> number; + count[number]++; + } + for (int number = 0; number <= 100; number++) { + if (count[number] == 0) continue; + if (answer == -1 || count[number] < count[answer]) + answer = number; + } + cout << answer << " " << count[answer]; + return 0; +} +``` + +**易错提醒:** 没出现过的数字次数为 0,但不能参加“最少出现”比较。 + +### 课后3:成绩分段【简单】 + +**题目描述:** 输入若干成绩,依次统计不及格、60~79、80~89、90~100 四个分数段的人数。 + +**输入格式:** 第一行输入学生人数;第二行输入每名学生的成绩。 + +**输出格式:** 依次输出四个分数段的人数。 + +**数据范围:** 学生人数为 1~1000,成绩为 0~100。 + +**输入样例:** +```text +6 +55 60 79 80 90 100 +``` + +**输出样例:** +```text +1 2 1 2 +``` + +**解题思路:** 先按具体分数计数,再按四个区间累加频次。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[101] = {0}, group[4] = {0}; + cin >> n; + for (int i = 0; i < n; i++) { + int score; + cin >> score; + count[score]++; + } + for (int score = 0; score <= 100; score++) { + if (score < 60) group[0] += count[score]; + else if (score < 80) group[1] += count[score]; + else if (score < 90) group[2] += count[score]; + else group[3] += count[score]; + } + cout << group[0] << " " << group[1] << " " + << group[2] << " " << group[3]; + return 0; +} +``` + +**易错提醒:** 分数段边界必须首尾相接,不能遗漏 60、80、90 和 100。 + +## 三、扩展练习(7题) + +### 扩展1:缺少的号码【基础综合】 + +**题目描述:** 1~若干号中每个号码本应出现一次,实际记录中缺少一个号码。输出缺少的号码。 + +**输入格式:** 第一行输入完整号码范围的上界;第二行输入其余所有号码。 + +**输出格式:** 缺少的号码。 + +**数据范围:** 上界为 2~1000。 + +**输入样例:** +```text +5 +1 2 5 3 +``` + +**输出样例:** +```text +4 +``` + +**解题思路:** 用计数数组记录出现情况,再寻找次数为 0 的号码。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int limit, count[1001] = {0}; + cin >> limit; + for (int i = 1; i < limit; i++) { + int number; + cin >> number; + count[number]++; + } + for (int number = 1; number <= limit; number++) { + if (count[number] == 0) { + cout << number; + break; + } + } + return 0; +} +``` + +**易错提醒:** 实际输入数量比完整范围少 1。 + +### 扩展2:字母频次表【基础综合】 + +**题目描述:** 输入一个只含大小写英文字母的字符串,忽略大小写,按字母顺序输出出现过的字母和次数。 + +**输入格式:** 输入一个不含空格的英文字母串。 + +**输出格式:** 每行输出一个小写字母和对应次数。 + +**数据范围:** 字符串长度为 1~500。 + +**输入样例:** +```text +GespG +``` + +**输出样例:** +```text +e 1 +g 2 +p 1 +s 1 +``` + +**解题思路:** 先统一为小写,再用字母减去小写字母起点得到 0~25 的下标。 + +**参考代码:** +```cpp +#include +#include +#include +using namespace std; + +int main() { + string s; + int count[26] = {0}; + cin >> s; + for (char c : s) { + c = (char)tolower(c); + count[c - 'a']++; + } + for (int i = 0; i < 26; i++) { + if (count[i] > 0) + cout << (char)('a' + i) << " " << count[i] << "\n"; + } + return 0; +} +``` + +**易错提醒:** 大写和小写统一后才能计入同一个位置。 + +### 扩展3:只出现一次的最小数【中等】 + +**题目描述:** 输入若干个 0~1000 的整数,输出只出现一次的最小数字;不存在时输出 `NONE`。 + +**输入格式:** 第一行输入整数数量;第二行输入所有整数。 + +**输出格式:** 满足条件的数字或 `NONE`。 + +**数据范围:** 整数数量为 1~2000。 + +**输入样例:** +```text +7 +5 2 5 3 8 2 9 +``` + +**输出样例:** +```text +3 +``` + +**解题思路:** 计数后从小到大寻找第一个次数等于 1 的下标。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[1001] = {0}, answer = -1; + cin >> n; + for (int i = 0; i < n; i++) { + int number; + cin >> number; + count[number]++; + } + for (int number = 0; number <= 1000; number++) { + if (count[number] == 1) { + answer = number; + break; + } + } + if (answer == -1) cout << "NONE"; + else cout << answer; + return 0; +} +``` + +**易错提醒:** “只出现一次”必须判断次数恰好等于 1。 + +### 扩展4:公共数字【中等】 + +**题目描述:** 输入两组 0~100 的整数,升序输出两组中都出现过的数字,每个数字只输出一次;没有则输出 `NONE`。 + +**输入格式:** 第一行输入第一组数量和所有数字;第二行输入第二组数量和所有数字。 + +**输出格式:** 两组共有的不同数字。 + +**数据范围:** 每组数量为 1~1000。 + +**输入样例:** +```text +5 1 2 2 5 8 +4 2 3 5 5 +``` + +**输出样例:** +```text +2 5 +``` + +**解题思路:** 分别用两个计数数组记录,两边次数都大于 0 的下标就是公共数字。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int firstCount[101] = {0}, secondCount[101] = {0}; + int n, number; + cin >> n; + for (int i = 0; i < n; i++) { + cin >> number; + firstCount[number]++; + } + cin >> n; + for (int i = 0; i < n; i++) { + cin >> number; + secondCount[number]++; + } + bool found = false; + for (int value = 0; value <= 100; value++) { + if (firstCount[value] > 0 && secondCount[value] > 0) { + if (found) cout << " "; + cout << value; + found = true; + } + } + if (!found) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 只判断是否出现,不按较小次数重复输出。 + +### 扩展5:两数之和查询【中等】 + +**题目描述:** 输入若干个 0~1000 的整数和目标和,判断能否从中选出两个不同位置的数,使它们之和等于目标。 + +**输入格式:** 第一行输入整数数量和目标和;第二行输入所有整数。 + +**输出格式:** 可以则输出 `YES`,否则输出 `NO`。 + +**数据范围:** 整数数量为 2~2000,目标和为 0~2000。 + +**输入样例:** +```text +5 10 +2 8 4 4 7 +``` + +**输出样例:** +```text +YES +``` + +**解题思路:** 计数后枚举第一个数,检查补数是否出现;两个数相同时必须至少出现两次。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, target, count[1001] = {0}; + cin >> n >> target; + for (int i = 0; i < n; i++) { + int number; + cin >> number; + count[number]++; + } + bool found = false; + for (int value = 0; value <= 1000; value++) { + int other = target - value; + if (other < 0 || other > 1000) continue; + if (value != other && count[value] > 0 && count[other] > 0) + found = true; + if (value == other && count[value] >= 2) + found = true; + } + cout << (found ? "YES" : "NO"); + return 0; +} +``` + +**易错提醒:** “不同位置”允许数值相同,但该数必须出现至少两次。 + +### 扩展6:频次最接近目标【略难】 + +**题目描述:** 输入若干个 0~100 的整数和目标次数,找出出现次数与目标次数差值最小且确实出现过的数字;并列取较小数字。 + +**输入格式:** 第一行输入整数数量和目标次数;第二行输入所有整数。 + +**输出格式:** 满足条件的数字及实际出现次数。 + +**数据范围:** 整数数量为 1~1000,目标次数为 0~1000。 + +**输入样例:** +```text +8 3 +1 1 2 2 2 2 5 5 +``` + +**输出样例:** +```text +1 2 +``` + +**解题思路:** 计数后对出现过的数字进行二次打擂台,比较频次与目标的距离。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + int n, target, count[101] = {0}, answer = -1; + cin >> n >> target; + for (int i = 0; i < n; i++) { + int number; + cin >> number; + count[number]++; + } + for (int value = 0; value <= 100; value++) { + if (count[value] == 0) continue; + if (answer == -1 || + abs(count[value] - target) < abs(count[answer] - target)) + answer = value; + } + cout << answer << " " << count[answer]; + return 0; +} +``` + +**易错提醒:** 没出现的数字不能参加比较;从小到大遍历且并列不更新即可保留较小数字。 + +### 扩展7:最多能配成多少对【略难】 + +**题目描述:** 输入若干只手套的颜色编号,每两只相同颜色手套可以配成一对。输出可配成的总对数、剩余单只数量,并按颜色升序输出每种颜色的对数。 + +**输入格式:** 第一行输入手套数量;第二行输入每只手套的颜色编号。 + +**输出格式:** 第一行输出总对数和剩余数量;随后输出每种能配对颜色的编号和对数。 + +**数据范围:** 手套数量为 1~2000,颜色编号为 1~100。 + +**输入样例:** +```text +8 +1 1 1 2 2 3 3 3 +``` + +**输出样例:** +```text +3 2 +1 1 +2 1 +3 1 +``` + +**解题思路:** 每种颜色的对数是次数除以 2,剩余数是次数除以 2 的余数,再分别累加。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, count[101] = {0}, pairs = 0, single = 0; + cin >> n; + for (int i = 0; i < n; i++) { + int color; + cin >> color; + count[color]++; + } + for (int color = 1; color <= 100; color++) { + pairs += count[color] / 2; + single += count[color] % 2; + } + cout << pairs << " " << single << "\n"; + for (int color = 1; color <= 100; color++) { + if (count[color] / 2 > 0) + cout << color << " " << count[color] / 2 << "\n"; + } + return 0; +} +``` + +**易错提醒:** 同一颜色出现三次只能组成一对并剩下一只。 diff --git a/第08课-枚举与模拟/教案-学生版.md b/第08课-枚举与模拟/教案-学生版.md new file mode 100644 index 0000000..fa8c6d6 --- /dev/null +++ b/第08课-枚举与模拟/教案-学生版.md @@ -0,0 +1,145 @@ +# 第8课:枚举与模拟(三级工具降维) + +> 等级说明:本课使用三级工具帮助解决一级、二级综合问题。 + +## 教学目标 +- 理解枚举算法的核心思想:列举所有可能,逐一验证 +- 掌握枚举优化的两个技巧:缩小范围、提前终止 +- 理解模拟算法的核心思想:按题目描述一步步翻译成代码 + +## 核心考点:枚举范围与模拟执行顺序 + +### 考什么 +枚举所有可能并逐一________,或按题目规则一步一步更新________。 + +### 必须理解 +枚举三步是:确定________、确定合法________、写出验证________。模拟时必须保持题目规定的________顺序。是否使用 `break`,取决于题目要________________。 + +### 核心写法 +```cpp +for (int i = ________; i <= ________; i++) { + if (____________________) { + // 记录、计数或输出 + } +} + +for (int i = 1; i <= ________; i++) { + // ____________________ +} +``` + +### 常见错误 +- 枚举范围漏掉端点会怎样?________________ +- 什么情况下不能找到答案就 `break`?________________ +- 模拟步骤颠倒是否会影响答案?________ + +### 判断是否掌握 +- [ ] 我能确定候选变量和完整范围。 +- [ ] 我能写出验证条件。 +- [ ] 我能按正确顺序模拟状态变化。 + +--- + +## 课堂编程题 + +### 课堂1:鸡兔同笼【适中】 + +**题目描述:** 笼中只有鸡和兔。输入头数与脚数,输出鸡和兔的数量;没有合法方案时输出 `NONE`。 + +**输入格式:** 输入动物总数和脚的总数。 + +**输出格式:** 输出鸡的数量和兔的数量,或输出 `NONE`。 + +**数据范围:** 动物总数为 1~1000,脚数为非负整数。 + +**输入样例:** +```text +10 28 +``` + +**输出样例:** +```text +6 4 +``` + +**解题思路:** 枚举鸡的数量,兔的数量由总数减去鸡数得到,再验证脚数。 + +**易错提醒:** 枚举范围必须包含 0 和动物总数;题目只需一个方案,找到后可以结束循环。 + +### 课堂2:硬币组合【适中】 + +**题目描述:** 用 1 元、2 元和 5 元硬币凑出指定金额,要求硬币总数固定。输出所有方案,每行依次输出三种硬币数量。 + +**输入格式:** 输入目标金额和硬币总数。 + +**输出格式:** 输出全部合法方案;没有方案时输出 `NONE`。 + +**数据范围:** 目标金额和硬币总数均为 1~100。 + +**输入样例:** +```text +10 4 +``` + +**输出样例:** +```text +1 2 1 +``` + +**解题思路:** 枚举 1 元和 2 元硬币数量,5 元硬币数量由总数直接计算,再检查金额。 + +**易错提醒:** 第三种硬币无需再枚举;题目要求全部方案,找到后不能提前结束。 + +### 课堂3:斐波那契数列【适中】 + +**题目描述:** 数列前两项为 1,之后每项等于前两项之和。输入项数,输出前若干项。 + +**输入格式:** 输入需要输出的项数。 + +**输出格式:** 输出数列前若干项,以空格分隔。 + +**数据范围:** 项数为 1~45。 + +**输入样例:** +```text +6 +``` + +**输出样例:** +```text +1 1 2 3 5 8 +``` + +**解题思路:** 用两个变量保存连续两项;先计算下一项,再按顺序更新两个变量。 + +**易错提醒:** 更新顺序不能颠倒,必须先用旧值计算下一项。 + +### 课堂4:交通灯状态【适中】 + +**题目描述:** 交通灯按"绿灯 30 秒、黄灯 5 秒、红灯 25 秒"循环。第 1 秒为绿灯。输入秒数,输出当时颜色以及该颜色已经持续的秒数。 + +**输入格式:** 输入一个正整数,表示经过的秒数。 + +**输出格式:** 输出颜色和当前颜色已持续的秒数。 + +**数据范围:** 秒数不超过十亿。 + +**输入样例:** +```text +34 +``` + +**输出样例:** +```text +YELLOW 4 +``` + +**解题思路:** 一个周期共 60 秒,将时间映射到 1~60,再按三个区间判断。 + +**易错提醒:** 第 60 秒仍在本周期,第 61 秒才回到绿灯第 1 秒。 + +--- + +## 课后作业 + +> 请完成本课对应的课后作业,见编程题文件。 diff --git a/第08课-枚举与模拟/教案-教师版.md b/第08课-枚举与模拟/教案-教师版.md new file mode 100644 index 0000000..5c22a92 --- /dev/null +++ b/第08课-枚举与模拟/教案-教师版.md @@ -0,0 +1,227 @@ +# 第8课:枚举与模拟(三级工具降维) + +> 等级说明:枚举与模拟属于 GESP 三级知识。本课把它们作为工具,用来简化一级、二级的循环、分支与简单数学问题。 + +## 教学目标 +- 理解枚举算法的核心思想:列举所有可能,逐一验证 +- 掌握枚举优化的两个技巧:缩小范围、提前终止 +- 理解模拟算法的核心思想:按题目描述一步步翻译成代码 + +## 核心考点:枚举范围与模拟执行顺序 + +### 考什么 +枚举题要求列出所有可能答案并逐一验证;模拟题要求按照题目描述复现一个变化过程。两者都依赖清晰的范围、条件和执行顺序,常考输出满足条件的方案、计数或最终状态。 + +### 必须理解 +枚举包含三步:确定候选变量、确定合法范围、写出验证条件。范围必须覆盖所有可能答案,但可以利用题目约束缩小。找到答案后是否 `break`,取决于题目要第一个答案还是全部答案。模拟则要把文字规则拆成按顺序执行的动作,明确每一步读取旧状态还是更新后的状态;若多个变量应同时变化,不能让前一个更新意外影响后一个计算。 + +### 核心写法 +```cpp +for (int candidate = lower; candidate <= upper; candidate++) { + if (满足全部条件) { + // 记录、计数或输出 candidate + } +} + +for (int step = 1; step <= totalSteps; step++) { + // 按题意顺序更新当前状态 +} +``` +枚举是在“答案空间”中找;模拟是在“时间步骤”中走。先判断题目属于哪一种,再确定循环变量的含义。 + +### 常见错误 +- 枚举范围少一个端点,漏掉合法答案。 +- 未判断题目要全部解,找到第一个就错误 `break`。 +- 把模拟步骤顺序颠倒,得到不同状态。 +- 明明能由约束缩小范围,却枚举过多无效候选,造成超时。 + +### 判断是否掌握 +能指出循环变量代表候选答案还是执行步数,能给出完整上下界和验证条件,并能用手算前几步检查模拟更新顺序。 + +--- + +## 题目一:鸡兔同笼(枚举入门) + +### 核心代码(一句话) +> 枚举的核心:**`for` 循环遍历所有可能的答案**,用 `if` 判断是否满足条件,满足就输出。 + +### 自然语言思路 +笼子里有鸡和兔,一共 n 个头、m 只脚。鸡有 2 只脚,兔有 4 只脚。问鸡和兔各有几只? + +第一步,鸡的数量可能是 0 到 n(因为总共 n 个头),让 `chicken` 从 0 循环到 n。 +第二步,兔的数量 = 总数 - 鸡数:`rabbit = n - chicken`。 +第三步,验证脚数:`chicken * 2 + rabbit * 4 == m`,如果成立,这就是答案。 +第四步,输出鸡和兔的数量。 + +> 枚举就像"地毯式搜索"——把所有可能性都试一遍,看哪个符合条件。 + +### 伪代码 +``` +1. 输入 n(头数), m(脚数) +2. for chicken = 0 到 n: +3. rabbit = n - chicken +4. if chicken * 2 + rabbit * 4 == m: +5. 输出 chicken 和 rabbit +``` + +### 真实代码 +```cpp +#include +using namespace std; + +int main() { + int n, m; + cin >> n >> m; + + for (int chicken = 0; chicken <= n; chicken++) { + int rabbit = n - chicken; + if (chicken * 2 + rabbit * 4 == m) { + cout << "鸡:" << chicken << ",兔:" << rabbit << endl; + } + } + return 0; +} +``` + +### 注意点 +1. **枚举范围 0 到 n**:不要漏掉 0(可能全是兔子)和 n(可能全是鸡)。 +2. **兔的数量直接算**:因为鸡兔总数固定,所以兔 = n - 鸡,不需要再套一层循环枚举兔。这是"间接枚举"——少一个变量就少一层循环。 +3. **可能无解**:如果循环结束都没输出,说明数据有问题(比如输入 2 个头 100 只脚)。 +4. **脚数奇偶性判断**:实际上可以先用 `if (m % 2 != 0)` 快速判断无解(因为每只动物脚数都是偶数),但这是优化技巧,初学者先掌握枚举思路。 + +--- + +## 题目二:找完数(枚举进阶) + +### 核心代码(一句话) +> 完数判断的核心:**枚举所有小于 n 的正整数 i,如果 `n % i == 0`,i 就是 n 的因子,把因子累加起来**,最后比较和是否等于 n。 + +### 自然语言思路 +完数是指一个数等于它所有真因子(不包括自身)之和。比如 6 = 1 + 2 + 3,28 = 1 + 2 + 4 + 7 + 14。请找出 1000 以内的所有完数。 + +第一步,外层枚举:`for (n = 2; n <= 1000; n++)`,对每个 n 判断它是不是完数。 +第二步,对每个 n,用内层循环从 1 到 n-1 找它的因子:`if (n % i == 0)` 则 `sum += i`。 +第三步,遍历完所有因子后,`if (sum == n)` 则 n 是完数,输出。 +第四步,优化:找因子只需要搜到 `n/2`(因为大于 n/2 的数不可能是 n 的因子,除了 n 自身)。不过用 `sqrt(n)` 也行但要处理成对出现的情况。 + +### 伪代码 +``` +1. for n = 2 到 1000: +2. sum = 0 +3. for i = 1 到 n-1: +4. if n % i == 0: +5. sum += i +6. if sum == n: +7. 输出 n +``` + +### 真实代码 +```cpp +#include +using namespace std; + +int main() { + for (int n = 2; n <= 1000; n++) { + int sum = 0; + // 找因子并累加(优化:只需搜到 n/2) + for (int i = 1; i <= n / 2; i++) { + if (n % i == 0) { + sum += i; + } + } + if (sum == n) { + cout << n << " "; // 输出:6 28 496 + } + } + cout << endl; + return 0; +} +``` + +### 注意点 +1. **sum 必须在内层循环前清零**:每次换一个 n,sum 要重新从 0 开始累加,忘了清零会累加前面所有数的结果。 +2. **优化枚举范围**:因子搜索范围从 `n-1` 优化到 `n/2`,因为大于 n/2 且小于 n 的数不可能是 n 的因子。 +3. **1 不是完数**:1 的真因子之和是 0(1 没有真因子),不等于 1,所以从 2 开始枚举。 +4. **1000 以内的完数只有 3 个**:6、28、496。如果输出不止这些,说明逻辑有误。 + +--- + +## 题目三:斐波那契数列(模拟算法) + +### 核心代码(一句话) +> 模拟的核心:**按照规则,用变量记录当前状态,一步一步更新**。斐波那契的规则是:**第三项 = 前两项之和**,用 `a, b` 两个变量滚动更新即可。 + +### 自然语言思路 +斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55……每一项等于前两项之和。输入 n,输出前 n 项。 + +第一步,用两个变量 `a` 和 `b` 分别记录"当前项"和"下一项",初始 `a = 1, b = 1`。 +第二步,循环 n 次,每次输出 `a`(当前项),然后计算新的两项:`temp = a + b; a = b; b = temp;`。 +第三步,模拟过程:a=1,b=1 → 输出1 → a=1,b=2 → 输出1 → a=2,b=3 → 输出2 → a=3,b=5 → 输出3 …… 就像"滚动更新"。 + +> 模拟算法 = 读懂题目规则 → 翻译成代码。不需要聪明,只需要认真。 + +### 伪代码 +``` +1. 输入 n +2. a = 1, b = 1 +3. for i = 1 到 n: +4. 输出 a +5. temp = a + b +6. a = b +7. b = temp +``` + +### 真实代码 +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + + int a = 1, b = 1; + for (int i = 1; i <= n; i++) { + cout << a << " "; + int temp = a + b; // 计算下一项 + a = b; // a 前进 + b = temp; // b 前进 + } + cout << endl; + return 0; +} +``` + +### 注意点 +1. **更新顺序不能错**:必须先算 `temp = a + b`(基于旧值),再更新 a 和 b。如果先改 a 再算 `a + b`,用的就是新的 a 了,结果全乱。 +2. **n=1 和 n=2 的情况**:循环 1 次输出 1,循环 2 次输出 "1 1"。代码不用特殊处理,天然正确。 +3. **数据范围**:斐波那契数列增长很快,第 46 项就超过 20 亿了。如果 n 大到 50+,要用 `long long` 替代 `int`(因为 `int` 最大约 21 亿)。 +4. **另一种写法 — 数组存储**:`int fib[100]; fib[1]=1; fib[2]=1; fib[i]=fib[i-1]+fib[i-2];` 更直观但占内存。 + +--- + +## 枚举 vs 模拟 对比 + +| 对比维度 | 枚举 | 模拟 | +|----------|------|------| +| 核心思路 | 列举所有可能 → 逐一验证 | 按照规则 → 逐步执行 | +| 适用场景 | 答案范围有限、可以逐一验证 | 题目有明确的步骤描述 | +| 典型题目 | 鸡兔同笼、百钱百鸡、完数 | 斐波那契、杨辉三角、日期计算 | +| 优化方向 | 缩小范围、提前终止、剪枝 | 找规律、用公式替代逐步模拟 | + +--- + +## 本课打油诗 + +> 枚举就是全试过,循环里面 if 判断。 +> 范围要想清边界,找到答案就停站。 +> 模拟照着步骤走,题目说啥咱干啥。 +> 变量更新看顺序,一步一步不出错。 + +--- + +## 课后作业 +1. 找出 1000 以内所有的"亲密数对"(a 的因子之和等于 b,b 的因子之和等于 a,且 a ≠ b) +2. 百钱百鸡:公鸡 5 元/只,母鸡 3 元/只,小鸡 1 元 3 只。用 100 元买 100 只鸡,输出所有方案 +3. 输出杨辉三角的前 n 行 +4. 输出所有三位数的水仙花数(如 153 = 1³ + 5³ + 3³) diff --git a/第08课-枚举与模拟/编程题-教师版.md b/第08课-枚举与模拟/编程题-教师版.md new file mode 100644 index 0000000..f0f6d46 --- /dev/null +++ b/第08课-枚举与模拟/编程题-教师版.md @@ -0,0 +1,676 @@ +# 第8课编程题(教师版) + +## 本课知识点覆盖表 + +| 核心知识点 | 等级定位 | 对应课堂题 | +|---|---|---| +| 单变量枚举、范围与验证条件 | 三级工具,服务一二级循环分支 | 课堂1 | +| 双变量枚举、利用约束缩小范围 | 三级工具,服务简单数学 | 课堂2 | +| 状态变量、按顺序更新 | 三级工具,服务循环模拟 | 课堂3 | +| 周期过程模拟、取模与分支 | 三级工具,服务综合判断 | 课堂4 | + +## 一、课堂练习(4题) + +### 课堂1:鸡兔同笼【适中】 + +**题目描述:** 笼中只有鸡和兔。输入头数与脚数,输出鸡和兔的数量;没有合法方案时输出 `NONE`。 + +**输入格式:** 输入动物总数和脚的总数。 + +**输出格式:** 输出鸡的数量和兔的数量,或输出 `NONE`。 + +**数据范围:** 动物总数为 1~1000,脚数为非负整数。 + +**输入样例:** +```text +10 28 +``` + +**输出样例:** +```text +6 4 +``` + +**解题思路:** 枚举鸡的数量,兔的数量由总数减去鸡数得到,再验证脚数。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int heads, feet; + cin >> heads >> feet; + bool found = false; + for (int chicken = 0; chicken <= heads; chicken++) { + int rabbit = heads - chicken; + if (chicken * 2 + rabbit * 4 == feet) { + cout << chicken << " " << rabbit; + found = true; + break; + } + } + if (!found) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 枚举范围必须包含 0 和动物总数;题目只需一个方案,找到后可以结束循环。 + +### 课堂2:硬币组合【适中】 + +**题目描述:** 用 1 元、2 元和 5 元硬币凑出指定金额,要求硬币总数固定。输出所有方案,每行依次输出三种硬币数量。 + +**输入格式:** 输入目标金额和硬币总数。 + +**输出格式:** 输出全部合法方案;没有方案时输出 `NONE`。 + +**数据范围:** 目标金额和硬币总数均为 1~100。 + +**输入样例:** +```text +10 4 +``` + +**输出样例:** +```text +1 2 1 +``` + +**解题思路:** 枚举 1 元和 2 元硬币数量,5 元硬币数量由总数直接计算,再检查金额。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int money, total; + cin >> money >> total; + bool found = false; + for (int one = 0; one <= total; one++) { + for (int two = 0; two + one <= total; two++) { + int five = total - one - two; + if (one + two * 2 + five * 5 == money) { + cout << one << " " << two << " " << five << "\n"; + found = true; + } + } + } + if (!found) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 第三种硬币无需再枚举;题目要求全部方案,找到后不能提前结束。 + +### 课堂3:斐波那契数列【适中】 + +**题目描述:** 数列前两项为 1,之后每项等于前两项之和。输入项数,输出前若干项。 + +**输入格式:** 输入需要输出的项数。 + +**输出格式:** 输出数列前若干项,以空格分隔。 + +**数据范围:** 项数为 1~45。 + +**输入样例:** +```text +6 +``` + +**输出样例:** +```text +1 1 2 3 5 8 +``` + +**解题思路:** 用两个变量保存连续两项;先计算下一项,再按顺序更新两个变量。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n; + long long first = 1, second = 1; + cin >> n; + for (int i = 1; i <= n; i++) { + if (i > 1) cout << " "; + cout << first; + long long next = first + second; + first = second; + second = next; + } + return 0; +} +``` + +**易错提醒:** 更新顺序不能颠倒,必须先用旧值计算下一项。 + +### 课堂4:交通灯状态【适中】 + +**题目描述:** 交通灯按“绿灯 30 秒、黄灯 5 秒、红灯 25 秒”循环。第 1 秒为绿灯。输入秒数,输出当时颜色以及该颜色已经持续的秒数。 + +**输入格式:** 输入一个正整数,表示经过的秒数。 + +**输出格式:** 输出颜色和当前颜色已持续的秒数。 + +**数据范围:** 秒数不超过十亿。 + +**输入样例:** +```text +34 +``` + +**输出样例:** +```text +YELLOW 4 +``` + +**解题思路:** 一个周期共 60 秒,将时间映射到 1~60,再按三个区间判断。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + long long second; + cin >> second; + int position = (second - 1) % 60 + 1; + if (position <= 30) + cout << "GREEN " << position; + else if (position <= 35) + cout << "YELLOW " << position - 30; + else + cout << "RED " << position - 35; + return 0; +} +``` + +**易错提醒:** 第 60 秒仍在本周期,第 61 秒才回到绿灯第 1 秒。 + +## 二、课后作业(3题) + +### 课后1:满足条件的倍数【简单】 + +**题目描述:** 输出指定范围内所有能被 3 整除但不能被 5 整除的整数。 + +**输入格式:** 输入范围的左端点和右端点。 + +**输出格式:** 按从小到大输出所有满足条件的整数;没有则输出 `NONE`。 + +**数据范围:** 左端点不大于右端点,绝对值不超过一万。 + +**输入样例:** +```text +1 20 +``` + +**输出样例:** +```text +3 6 9 12 18 +``` + +**解题思路:** 枚举范围内每个整数,用一个条件验证。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int left, right; + bool found = false; + cin >> left >> right; + for (int number = left; number <= right; number++) { + if (number % 3 == 0 && number % 5 != 0) { + if (found) cout << " "; + cout << number; + found = true; + } + } + if (!found) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 范围包含两个端点;两个条件必须同时满足。 + +### 课后2:小球反弹高度【简单】 + +**题目描述:** 小球从指定高度落下,每次反弹到上一次高度的一半。输入初始高度和反弹次数,输出最后一次反弹高度,保留两位小数。 + +**输入格式:** 输入初始高度和反弹次数。 + +**输出格式:** 输出最后一次反弹高度,保留两位小数。 + +**数据范围:** 初始高度为正数,反弹次数为 0~30。 + +**输入样例:** +```text +100 3 +``` + +**输出样例:** +```text +12.50 +``` + +**解题思路:** 每次循环把当前高度除以 2,按步骤模拟。 + +**参考代码:** +```cpp +#include +#include +using namespace std; + +int main() { + double height; + int times; + cin >> height >> times; + for (int i = 1; i <= times; i++) height /= 2; + cout << fixed << setprecision(2) << height; + return 0; +} +``` + +**易错提醒:** 反弹 0 次时高度保持初始值。 + +### 课后3:机器人走直线【简单】 + +**题目描述:** 机器人初始位置为 0。输入若干条指令,正数表示向右移动,负数表示向左移动。输出最终位置。 + +**输入格式:** 第一行输入指令数量;第二行输入每条指令的移动距离。 + +**输出格式:** 输出机器人最终位置。 + +**数据范围:** 指令数量为 1~100,每次移动距离绝对值不超过一千。 + +**输入样例:** +```text +5 +3 -2 4 -6 1 +``` + +**输出样例:** +```text +0 +``` + +**解题思路:** 用一个变量记录位置,按输入顺序累加每次移动。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, position = 0; + cin >> n; + for (int i = 0; i < n; i++) { + int move; + cin >> move; + position += move; + } + cout << position; + return 0; +} +``` + +**易错提醒:** 负数已经表示向左,不需要再额外减一次。 + +## 三、扩展练习(7题) + +### 扩展1:三位水仙花数【基础综合】 + +**题目描述:** 输出指定范围内所有三位水仙花数。水仙花数等于各位数字立方和。 + +**输入格式:** 输入范围的左端点和右端点。 + +**输出格式:** 输出所有水仙花数;没有则输出 `NONE`。 + +**数据范围:** 输入范围位于 100~999。 + +**输入样例:** +```text +100 400 +``` + +**输出样例:** +```text +153 370 371 +``` + +**解题思路:** 枚举每个三位数,拆出百位、十位和个位后验证。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int left, right; + bool found = false; + cin >> left >> right; + for (int number = left; number <= right; number++) { + int a = number / 100; + int b = number / 10 % 10; + int c = number % 10; + if (a * a * a + b * b * b + c * c * c == number) { + if (found) cout << " "; + cout << number; + found = true; + } + } + if (!found) cout << "NONE"; + return 0; +} +``` + +**易错提醒:** 十位数字应先除以 10 再取余数。 + +### 扩展2:百钱买百鸡【中等】 + +**题目描述:** 公鸡每只 5 元,母鸡每只 3 元,小鸡 3 只 1 元。用 100 元恰好买 100 只鸡,输出全部方案。 + +**输入格式:** 本题没有输入。 + +**输出格式:** 每行依次输出公鸡、母鸡和小鸡数量。 + +**数据范围:** 三种鸡的数量均为非负整数。 + +**输入样例:** +```text +无 +``` + +**输出样例:** +```text +0 25 75 +4 18 78 +8 11 81 +12 4 84 +``` + +**解题思路:** 枚举公鸡和母鸡,小鸡由总数计算;为避免小数,用“小鸡数量能被 3 整除”验证价格。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + for (int rooster = 0; rooster <= 20; rooster++) { + for (int hen = 0; hen <= 33; hen++) { + int chick = 100 - rooster - hen; + if (chick >= 0 && chick % 3 == 0 && + rooster * 5 + hen * 3 + chick / 3 == 100) { + cout << rooster << " " << hen << " " << chick << "\n"; + } + } + } + return 0; +} +``` + +**易错提醒:** 小鸡数量必须非负且能被 3 整除。 + +### 扩展3:数字谜【中等】 + +**题目描述:** 找出所有两位数,使它等于各位数字之和的 4 倍,并按从小到大输出。 + +**输入格式:** 本题没有输入。 + +**输出格式:** 输出全部满足条件的两位数。 + +**数据范围:** 枚举 10~99。 + +**输入样例:** +```text +无 +``` + +**输出样例:** +```text +12 24 36 48 +``` + +**解题思路:** 枚举两位数并拆分十位和个位,验证等式。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + bool first = true; + for (int number = 10; number <= 99; number++) { + int tens = number / 10; + int ones = number % 10; + if (number == 4 * (tens + ones)) { + if (!first) cout << " "; + cout << number; + first = false; + } + } + return 0; +} +``` + +**易错提醒:** 枚举顺序已经从小到大,不需要任何排序。 + +### 扩展4:日期的下一天【中等】 + +**题目描述:** 输入一个日期,输出下一天。需要正确处理月末、年末和闰年二月。 + +**输入格式:** 输入年份、月份和日期。 + +**输出格式:** 输出下一天的年份、月份和日期。 + +**数据范围:** 输入保证是合法日期,年份为 1~9999。 + +**输入样例:** +```text +2024 2 29 +``` + +**输出样例:** +```text +2024 3 1 +``` + +**解题思路:** 先根据月份和闰年确定本月天数,日期加一后按顺序处理月进位和年进位。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int year, month, day; + cin >> year >> month >> day; + int days; + if (month == 2) { + bool leap = year % 400 == 0 || + (year % 4 == 0 && year % 100 != 0); + days = leap ? 29 : 28; + } else if (month == 4 || month == 6 || + month == 9 || month == 11) { + days = 30; + } else { + days = 31; + } + day++; + if (day > days) { + day = 1; + month++; + if (month > 12) { + month = 1; + year++; + } + } + cout << year << " " << month << " " << day; + return 0; +} +``` + +**易错提醒:** 必须先处理日期进位,再处理月份和年份进位。 + +### 扩展5:比赛积分模拟【中等】 + +**题目描述:** 一支球队进行若干场比赛,胜得 3 分、平得 1 分、负得 0 分。输入每场进球数和失球数,输出总积分及胜、平、负场次。 + +**输入格式:** 第一行输入比赛场数;随后每行输入一场比赛的进球数和失球数。 + +**输出格式:** 依次输出总积分、胜场、平场和负场。 + +**数据范围:** 比赛场数为 1~100,进球数和失球数为非负整数。 + +**输入样例:** +```text +3 +2 1 +0 0 +1 3 +``` + +**输出样例:** +```text +4 1 1 1 +``` + +**解题思路:** 按场次模拟,根据比分三分支更新积分和统计量。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, points = 0, win = 0, draw = 0, lose = 0; + cin >> n; + for (int i = 0; i < n; i++) { + int scored, conceded; + cin >> scored >> conceded; + if (scored > conceded) { + points += 3; + win++; + } else if (scored == conceded) { + points++; + draw++; + } else { + lose++; + } + } + cout << points << " " << win << " " << draw << " " << lose; + return 0; +} +``` + +**易错提醒:** 三种比赛结果互斥,使用多层分支。 + +### 扩展6:约瑟夫报数简化版【略难】 + +**题目描述:** 若干名同学围成一圈,从 1 号开始依次报数,报到指定数字的人离开,继续从下一人报数,直到只剩一人。输出最后留下的编号。 + +**输入格式:** 输入同学人数和离开所对应的报数。 + +**输出格式:** 输出最后留下的同学编号。 + +**数据范围:** 同学人数为 1~100,报数为 1~100。 + +**输入样例:** +```text +5 3 +``` + +**输出样例:** +```text +4 +``` + +**解题思路:** 用数组记录是否仍在圈中,循环移动当前位置,只对未离开的人计数,报到指定数字时标记离开。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, limit; + bool alive[101] = {false}; + cin >> n >> limit; + for (int i = 1; i <= n; i++) alive[i] = true; + int remaining = n, position = 0, count = 0; + while (remaining > 1) { + position = position % n + 1; + if (!alive[position]) continue; + count++; + if (count == limit) { + alive[position] = false; + remaining--; + count = 0; + } + } + for (int i = 1; i <= n; i++) + if (alive[i]) cout << i; + return 0; +} +``` + +**易错提醒:** 离开的人不再参与报数;位置到末尾后要回到 1 号。 + +### 扩展7:自动售货机【略难】 + +**题目描述:** 售货机初始余额为 0。依次处理若干操作:正数表示投入金额;-1 表示购买 3 元商品;-2 表示购买 5 元商品。余额足够才扣款并增加购买次数,否则记录一次失败。输出最终余额、成功购买次数和失败次数。 + +**输入格式:** 第一行输入操作数量;第二行输入所有操作代码。 + +**输出格式:** 依次输出最终余额、成功购买次数和失败次数。 + +**数据范围:** 操作数量为 1~1000,投入金额为 1~100。 + +**输入样例:** +```text +7 +5 -1 -2 8 -2 -1 1 +``` + +**输出样例:** +```text +3 3 1 +``` + +**解题思路:** 按输入顺序模拟。投入金额直接增加余额;购买操作先确定价格,再判断余额是否足够。 + +**参考代码:** +```cpp +#include +using namespace std; + +int main() { + int n, balance = 0, success = 0, failure = 0; + cin >> n; + for (int i = 0; i < n; i++) { + int operation; + cin >> operation; + if (operation > 0) { + balance += operation; + } else { + int price = (operation == -1) ? 3 : 5; + if (balance >= price) { + balance -= price; + success++; + } else { + failure++; + } + } + } + cout << balance << " " << success << " " << failure; + return 0; +} +``` + +**易错提醒:** 失败购买不能扣除余额;操作必须严格按输入顺序处理。 diff --git a/第11课-GESP二级真题训练二/教案-学生版.md b/第11课-GESP二级真题训练二/教案-学生版.md new file mode 100644 index 0000000..8055c68 --- /dev/null +++ b/第11课-GESP二级真题训练二/教案-学生版.md @@ -0,0 +1,182 @@ +# 第11课:综合练习 + +## 教学目标 +- 混合题型训练,覆盖一二级核心知识点 +- 查漏补缺,找到薄弱环节 +- 提高解题速度和准确率 + +## 核心考点:混合题的拆解与算法组合 + +### 考什么 +从综合题中识别多个________,为它们选择已经学过的代码模板并正确组合。 + +### 必须理解 +先把题目拆成“________→________→________”。看到“每个”常想到________;看到“最大/最小”想到________;看到“出现次数”想到________;看到“如果/否则”想到________。 + +### 核心写法 +```text +1. 圈出输入、输出和________ +2. 把处理过程拆成若干________ +3. 为每个任务选择________ +4. 确定步骤顺序和________变量 +5. 用样例与________值检查 +``` + +### 常见错误 +- 为什么不能读完题就立刻写代码?________________ +- 下标、数值和次数混用会造成什么问题?____________ +- 除了题目样例,还应测试哪些情况?________________ + +### 判断是否掌握 +- [ ] 我能写出输入—处理—输出结构。 +- [ ] 我能把处理过程拆成小任务。 +- [ ] 我能说明每一步使用的代码模板。 + +--- + +## 题目一:成绩等级判断(分支 + 输入输出) + +### 核心代码(一句话) +> 多分支判断的核心:**`______` 链条**,从大到小或从小到大依次判断,每种情况一个分支。 + +### 自然语言思路 + + + +### 伪代码 +``` +1. 输入 score +2. if score >= 90: 输出 ______ +3. else if score >= 80: 输出 ______ +4. else if score >= 70: 输出 ______ +5. else if score >= 60: 输出 ______ +6. else: 输出 ______ +``` + +### 真实代码 +```cpp + +``` + +### 注意点 + + + +--- + +## 题目二:数组求最大值及下标(数组 + 循环 + 打擂台) + +### 核心代码(一句话) +> 带下标记录的打擂台:**用一个变量记______,另一个变量记______**,每次换擂主时两个一起更新。 + +### 自然语言思路 + + + +### 伪代码 +``` +1. 输入 n +2. 读入数组 arr[n] +3. maxVal = ______, maxIndex = ______ +4. for i = ______ 到 ______: +5. if arr[i] ______ maxVal: // 严格大于 +6. maxVal = ______ +7. maxIndex = ______ +8. 输出 maxVal 和 maxIndex +``` + +### 真实代码 +```cpp + +``` + +### 注意点 + + + +--- + +## 题目三:字符串分类统计(string + 字符判断 + 多计数器) + +### 核心代码(一句话) +> 字符串分类统计的核心:**______ + 多分支判断 + 多个计数器**,每个 `if` 分支对应一个计数器。 + +### 自然语言思路 + + + +### 伪代码 +``` +1. ______ 读取字符串 s +2. cnt1 = 0, cnt2 = 0, cnt3 = 0, cnt4 = 0 +3. for i = 0 到 ______: +4. if ______(s[i]): cnt1++ +5. else if ______(s[i]): cnt2++ +6. else if ______(s[i]): cnt3++ +7. else: cnt4++ +8. 输出四个计数结果 +``` + +### 真实代码 +```cpp + +``` + +### 注意点 + + + +--- + +## 限时练习(课堂完成) + +### 练习组一(限时 30 分钟) +1. 输入两个数,输出较大的数 +2. 输入 n,输出 n 的阶乘 +3. 输入一个字符串,判断是否为回文 + +### 练习组二(限时 30 分钟) +1. 输入 n 个数,求平均值 +2. 找出 100 以内的所有素数 +3. 输入一个正整数,输出它的逆序数 + +### 练习组三(限时 30 分钟) +1. 输入年份,判断是否为闰年 +2. 输入 n,打印左下直角三角形 +3. 输入一个字符串,统计其中数字的个数 + +--- + +## 解题技巧 + +### 1. 仔细审题 + + +### 2. 先想后写 + + +### 3. 测试验证 + + +### 4. 时间管理 + + +--- + +## 常见错误检查清单 + +- [ ] 头文件是否包含 +- [ ] `using namespace std;` 是否写了 +- [ ] 变量是否初始化 +- [ ] 数组下标是否越界 +- [ ] 循环边界是否正确 +- [ ] 输出格式是否正确 +- [ ] 是否处理了边界情况 +- [ ] 整数除法是否需要转浮点数 +- [ ] string 输入是否用了 `getline`(含空格时) + +--- + +## 课后作业 + +> 请完成本课对应的课后作业,见编程题文件。 diff --git a/第11课-GESP二级真题训练二/教案-教师版.md b/第11课-GESP二级真题训练二/教案-教师版.md new file mode 100644 index 0000000..d3323dc --- /dev/null +++ b/第11课-GESP二级真题训练二/教案-教师版.md @@ -0,0 +1,292 @@ +# 第11课:综合练习 + +## 教学目标 +- 混合题型训练,覆盖一二级核心知识点 +- 查漏补缺,找到薄弱环节 +- 提高解题速度和准确率 + +## 核心考点:混合题的拆解与算法组合 + +### 考什么 +综合题不会直接告诉你使用哪一课的知识,而是把输入输出、分支、循环、数组、字符串等组合起来。考试重点是从题意中识别若干小任务,为每个小任务选择已经学过的代码模式,再按正确顺序连接。 + +### 必须理解 +先把题目拆成“输入什么→处理什么→输出什么”三段。处理部分继续拆成动作,例如“遍历数组→筛选满足条件的元素→打擂台求最值→输出下标”。每个动作只解决一个问题,再把前一步结果交给下一步。读到“每个”通常想到遍历,读到“最大/最小”想到打擂台,读到“出现次数”想到计数,读到“如果/否则”想到分支。组合算法时要明确数据在各步骤中的含义,不能边读题边盲写代码。 + +### 核心写法 +```text +1. 圈出输入、输出和数据范围 +2. 把处理过程拆成若干小任务 +3. 为每个小任务选择代码模板 +4. 确定各步骤先后与中间变量 +5. 写代码后用样例和边界值检查 +``` +例如“找数组中出现次数最多的数”可拆成:数组输入 + 频次统计 + 打擂台 + 输出答案。 + +### 常见错误 +- 一看到长题目就直接写代码,没有先拆任务。 +- 只完成主算法,漏掉输入格式、并列规则或输出顺序。 +- 不区分中间变量的含义,把下标、数值和次数混用。 +- 只测试题目样例,不测试空边界、最小值、最大值或并列情况。 + +### 判断是否掌握 +拿到一道混合题,能在动手前写出清晰的三到五步伪代码,并能指出每一步使用了哪种基础结构或算法模板。 + +--- + +## 题目一:成绩等级判断(分支 + 输入输出) + +### 核心代码(一句话) +> 多分支判断的核心:**`if - else if - else` 链条**,从大到小或从小到大依次判断,每种情况一个分支。 + +### 自然语言思路 +输入一个 0-100 的成绩,输出对应的等级:90-100 → A,80-89 → B,70-79 → C,60-69 → D,60以下 → E。 + +第一步,接收输入 `score`。 +第二步,从高分往低分判断:`if (score >= 90)` → A,`else if (score >= 80)` → B,依次类推。 +第三步,最后一个 `else` 兜底,对应 E 等级。 +第四步,注意判断顺序:如果从小到大判断(先 `score < 60`),要看清楚边界别重叠。 + +> GESP 考试中这类题会以"程序阅读"或"编程题"形式出现,要注意 if-else 的匹配逻辑。 + +### 伪代码 +``` +1. 输入 score +2. if score >= 90: 输出 "A" +3. else if score >= 80: 输出 "B" +4. else if score >= 70: 输出 "C" +5. else if score >= 60: 输出 "D" +6. else: 输出 "E" +``` + +### 真实代码 +```cpp +#include +using namespace std; + +int main() { + int score; + cin >> score; + + if (score >= 90) { + cout << "A" << endl; + } else if (score >= 80) { + cout << "B" << endl; + } else if (score >= 70) { + cout << "C" << endl; + } else if (score >= 60) { + cout << "D" << endl; + } else { + cout << "E" << endl; + } + return 0; +} +``` + +### 注意点 +1. **判断顺序很重要**:从大到小写最清晰,因为 `else if` 意味着"前面条件都不满足"。如果从小到大写,要注意边界。 +2. **边界值**:90 是 A,89 是 B。如果写成 `score > 90` 就会把 90 分的人划到 B,检查时要留意。 +3. **输入范围**:题目保证输入在 0-100,但实际考试中如果输入 101 按上面代码会输出 "A"(因为 ≥90)。严谨的做法是加范围检查。 + +--- + +## 题目二:数组求最大值及下标(数组 + 循环 + 打擂台) + +### 核心代码(一句话) +> 带下标记录的打擂台:**用一个变量记最大值,另一个变量记下标**,每次换擂主时两个一起更新。 + +### 自然语言思路 +输入 n 个数,输出最大值和它第一次出现的下标。 + +第一步,读入数组。 +第二步,设 `maxVal = arr[0]`,`maxIndex = 0`,两个变量一起初始化为第一个元素的值和下标。 +第三步,从 `i = 1` 开始遍历,如果 `arr[i] > maxVal`(严格大于),则同时更新 `maxVal = arr[i]` 和 `maxIndex = i`。 +第四步,遍历结束,输出最大值和下标。 + +> 注意是"严格大于" `>`,不是 `>=`。如果写成 `>=`,遇到重复最大值时会记录最后一次出现的位置。 + +### 伪代码 +``` +1. 输入 n +2. 读入数组 arr[n] +3. maxVal = arr[0], maxIndex = 0 +4. for i = 1 到 n-1: +5. if arr[i] > maxVal: // 严格大于 +6. maxVal = arr[i] +7. maxIndex = i +8. 输出 maxVal 和 maxIndex +``` + +### 真实代码 +```cpp +#include +using namespace std; + +int main() { + int n; + cin >> n; + int arr[n]; + for (int i = 0; i < n; i++) { + cin >> arr[i]; + } + + int maxVal = arr[0]; + int maxIndex = 0; + for (int i = 1; i < n; i++) { + if (arr[i] > maxVal) { + maxVal = arr[i]; + maxIndex = i; + } + } + + cout << "最大值:" << maxVal << ",下标:" << maxIndex << endl; + return 0; +} +``` + +### 注意点 +1. **`>` vs `>=`**:用 `>` 保留第一次出现的下标,用 `>=` 保留最后一次出现的下标。看题目要求。 +2. **maxIndex 初始值**:必须和 maxVal 的初始值对应。如果 maxVal 从 arr[0] 开始,maxIndex 就是 0。 +3. **循环从 1 开始**:因为 arr[0] 已经是初始擂主了。 + +--- + +## 题目三:字符串分类统计(string + 字符判断 + 多计数器) + +### 核心代码(一句话) +> 字符串分类统计的核心:**遍历 + 多分支判断 + 多个计数器**,每个 `if` 分支对应一个计数器。 + +### 自然语言思路 +输入一行字符串,统计其中大写字母、小写字母、数字、其他字符(含空格)各有多少个。 + +第一步,用 `getline(cin, s)` 读入整行(因为可能含空格)。 +第二步,设四个计数器 `upper, lower, digit, other` 都初始为 0。 +第三步,遍历每个字符 `s[i]`,用 `isupper`、`islower`、`isdigit` 判断类型,对应计数器 +1。 +第四步,都不满足的归入 `other`。 +第五步,输出四个统计结果。 +第六步,注意空格也算"其他",如果不想算空格可以先单独判断 `s[i] == ' '`。 + +### 伪代码 +``` +1. getline 读取字符串 s +2. upper = 0, lower = 0, digit = 0, other = 0 +3. for i = 0 到 s.length()-1: +4. if isupper(s[i]): upper++ +5. else if islower(s[i]): lower++ +6. else if isdigit(s[i]): digit++ +7. else: other++ +8. 输出四个计数结果 +``` + +### 真实代码 +```cpp +#include +#include +#include +using namespace std; + +int main() { + string s; + getline(cin, s); + + int upper = 0, lower = 0, digit = 0, other = 0; + for (int i = 0; i < s.length(); i++) { + if (isupper(s[i])) { + upper++; + } else if (islower(s[i])) { + lower++; + } else if (isdigit(s[i])) { + digit++; + } else { + other++; + } + } + + cout << "大写:" << upper + << ",小写:" << lower + << ",数字:" << digit + << ",其他:" << other << endl; + return 0; +} +``` + +### 注意点 +1. **`getline` 不是 `cin`**:`cin >> s` 遇空格就停,"Hello World" 只会读到 "Hello"。 +2. **`isupper` 等函数需要 ``**:别忘了加这个头文件。 +3. **else 分支的归属**:空格、标点符号都会进 `other`。如果题目要求单独统计空格,需要在 `else` 前加 `else if (s[i] == ' ')`。 +4. **空的字符串**:如果什么都没输入,四个计数器都是 0,输出全零,不会出错。 + +--- + +## 限时练习(课堂完成) + +### 练习组一(限时 30 分钟) +1. 输入两个数,输出较大的数(基础输入输出 + if) +2. 输入 n,输出 n 的阶乘 `1 × 2 × 3 × ... × n`(循环 + 累乘) +3. 输入一个字符串,判断是否为回文(string + 循环) + +### 练习组二(限时 30 分钟) +1. 输入 n 个数,求平均值(数组 + 求和 + 浮点数) +2. 找出 100 以内的所有素数(循环 + 判断素数) +3. 输入一个正整数,输出它的逆序数(如 12345 → 54321)(数位拆解) + +### 练习组三(限时 30 分钟) +1. 输入年份,判断是否为闰年(能被 4 整除且不能被 100 整除,或能被 400 整除) +2. 输入 n,打印左下直角三角形(嵌套循环) +3. 输入一个字符串,统计其中数字的个数(string + isdigit) + +--- + +## 解题技巧 + +### 1. 仔细审题 +- 看清输入输出格式(空格、换行、大小写) +- 注意数据范围(决定用 `int` 还是 `long long`) +- 理解题目要求(从小到大输出还是从大到小) + +### 2. 先想后写 +- 在草稿纸上写出核心步骤 +- 用伪代码搭好骨架 +- 再动手写真实代码 + +### 3. 测试验证 +- 先用样例数据测试 +- 再试边界情况(0、1、最大值、最小值) +- 检查输出格式是否完全一致 + +### 4. 时间管理 +- 先做简单的题,拿稳基础分 +- 不会的题先跳过,不要死磕 +- 留 10 分钟检查 + +--- + +## 常见错误检查清单 + +在交卷前逐条检查: + +- [ ] 头文件是否包含(``、``、``、`` 等) +- [ ] `using namespace std;` 是否写了 +- [ ] 变量是否初始化(特别是累加器、计数器) +- [ ] 数组下标是否越界(从 0 到 n-1) +- [ ] 循环边界是否正确(`<` vs `<=`) +- [ ] 输出格式是否正确(空格、换行、中文/英文标点) +- [ ] 是否处理了边界情况(0、1、负数等) +- [ ] 整数除法是否需要转浮点数 +- [ ] string 输入是否用了 `getline`(含空格时) + +--- + +## 本课打油诗 + +> 综合练习查漏洞,各种题型过一遍。 +> 先审题意后动手,伪代码里思路现。 +> 边界条件要想全,样例测完再交卷。 +> 时间分配有策略,不会先跳回头见。 + +--- + +## 课后作业 +1. 补完课堂限时练习中未完成的题目 +2. 整理自己的错题本,写出错误原因和正确解法 +3. 在洛谷上完成 GESP 一级真题中的 3 道编程题