init: 12 lessons + outline
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -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
|
||||
204
GESP暑假班大纲.md
Normal file
204
GESP暑假班大纲.md
Normal file
@@ -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` |
|
||||
| 头文件 | `<cmath>`、`<algorithm>`、`<cctype>` |
|
||||
|
||||
**考点**:函数名、头文件、参数类型、返回值
|
||||
|
||||
---
|
||||
|
||||
### 第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分
|
||||
181
第01课-C++自带函数/教案-学生版.md
Normal file
181
第01课-C++自带函数/教案-学生版.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# 第1课:C++自带函数
|
||||
|
||||
## 教学目标
|
||||
- 学会查阅和使用C++标准库自带函数
|
||||
- 掌握 `<cmath>`、`<algorithm>`、`<cctype>` 三个常用头文件
|
||||
- 能用自带函数解决计算问题,体会"站在巨人的肩膀上"
|
||||
|
||||
## 核心考点:正确选择并使用标准库函数
|
||||
|
||||
### 考什么
|
||||
根据题目任务选择正确的________,同时写对________、参数和返回值。
|
||||
|
||||
### 必须理解
|
||||
调用函数要过三关:它属于哪个________;传入什么________;返回什么________。
|
||||
|
||||
### 核心写法
|
||||
```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` 返回浮点数;格式化输出需要包含 `<iomanip>`。
|
||||
|
||||
---
|
||||
|
||||
### 课堂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`。
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
|
||||
> 请完成本课对应的课后作业,见编程题文件。
|
||||
187
第01课-C++自带函数/教案-教师版.md
Normal file
187
第01课-C++自带函数/教案-教师版.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# 第1课:C++自带函数
|
||||
|
||||
## 教学目标
|
||||
- 学会查阅和使用C++标准库自带函数
|
||||
- 掌握 `<cmath>`、`<algorithm>`、`<cctype>` 三个常用头文件
|
||||
- 能用自带函数解决计算问题,体会"站在巨人的肩膀上"
|
||||
|
||||
## 核心考点:正确选择并使用标准库函数
|
||||
|
||||
### 考什么
|
||||
题目通常给出一个计算或处理任务,要求选择合适的标准库函数,并写对头文件、参数和返回值。例如开平方用 `sqrt`,求最大值用 `max`,判断字符用 `isdigit`。考试不只考“记住函数名”,还考能否把函数正确放进完整程序。
|
||||
|
||||
### 必须理解
|
||||
调用标准库函数要过三关:第一,函数属于哪个头文件;第二,传入参数的类型和含义;第三,返回结果是什么类型。例如 `sqrt(x)` 返回浮点数,即使 x 是整数,也要注意精度和类型。
|
||||
|
||||
### 核心写法
|
||||
```cpp
|
||||
#include <cmath> // sqrt、pow
|
||||
#include <algorithm> // max、min、swap
|
||||
#include <cctype> // isdigit、isalpha
|
||||
|
||||
double root = sqrt(x);
|
||||
int bigger = max(a, b);
|
||||
```
|
||||
先根据任务选函数,再补头文件,最后检查参数范围和返回类型。
|
||||
|
||||
### 常见错误
|
||||
- 忘记包含对应头文件,导致函数未声明。
|
||||
- 认为 `pow` 一定返回整数,直接用于要求精确整数的运算,可能受到浮点误差影响。
|
||||
- 只背函数名,不检查参数是否满足要求,例如对负数直接开平方。
|
||||
|
||||
### 判断是否掌握
|
||||
看到“开平方、绝对值、最值、交换、字符分类”时,能说出函数名、头文件、关键参数和返回结果,并能独立写出一段正确调用代码。
|
||||
|
||||
---
|
||||
|
||||
## 函数速查表
|
||||
|
||||
### 一、数学计算函数(头文件:`<cmath>`)
|
||||
|
||||
| 函数 | 参数 | 返回值 | 示例 | 说明 |
|
||||
|------|------|--------|------|------|
|
||||
| `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ᵇ |
|
||||
|
||||
### 二、最值与交换函数(头文件:`<algorithm>`)
|
||||
|
||||
| 函数 | 参数 | 返回值 | 示例 | 说明 |
|
||||
|------|------|--------|------|------|
|
||||
| `max(a, b)` | 两个同类型的值 | 同类型 — 较大值 | `max(3, 7)` → `7` | 两数取大 |
|
||||
| `min(a, b)` | 两个同类型的值 | 同类型 — 较小值 | `min(3, 7)` → `3` | 两数取小 |
|
||||
| `swap(a, b)` | 两个同类型的**变量** | `void`(无返回值) | `swap(x, y)` → x和y交换 | 交换两变量的值 |
|
||||
|
||||
### 三、字符判断函数(头文件:`<cctype>`)
|
||||
|
||||
| 函数 | 参数 | 返回值 | 示例 | 说明 |
|
||||
|------|------|--------|------|------|
|
||||
| `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) |
|
||||
|
||||
### 四、字符转换函数(头文件:`<cctype>`)
|
||||
|
||||
| 函数 | 参数 | 返回值 | 示例 | 说明 |
|
||||
|------|------|--------|------|------|
|
||||
| `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 <iostream>
|
||||
#include <cmath> // sqrt、pow 在这里
|
||||
#include <iomanip> // 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` 在 `<cmath>` 里,格式化输出在 `<iomanip>` 里。
|
||||
2. **pow 的第二个参数**:`pow(底数, 指数)`,指数写 `2` 表示平方,写 `0.5` 也能表示开方(和 sqrt 等价)。
|
||||
3. **坐标相减顺序无所谓**:`pow(x1-x2, 2)` 和 `pow(x2-x1, 2)` 结果一样,因为平方消掉了负号。
|
||||
4. **保留小数的另一种写法**:`printf("%.2f", dist)` 也可以,需要 `<cstdio>`。
|
||||
|
||||
---
|
||||
|
||||
## 题目二:(待橙子补充)
|
||||
|
||||
> 建议覆盖:`max/min`、`swap` 等 `<algorithm>` 函数
|
||||
|
||||
---
|
||||
|
||||
### 核心代码(一句话)
|
||||
>
|
||||
|
||||
### 自然语言思路
|
||||
|
||||
|
||||
### 伪代码
|
||||
|
||||
|
||||
### 真实代码
|
||||
```cpp
|
||||
|
||||
```
|
||||
|
||||
### 注意点
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 题目三:(待橙子补充)
|
||||
|
||||
> 建议覆盖:`isalpha`、`isdigit`、`islower`、`isupper`、`tolower`、`toupper` 等 `<cctype>` 函数
|
||||
|
||||
---
|
||||
|
||||
### 核心代码(一句话)
|
||||
>
|
||||
|
||||
### 自然语言思路
|
||||
|
||||
|
||||
### 伪代码
|
||||
|
||||
|
||||
### 真实代码
|
||||
```cpp
|
||||
|
||||
```
|
||||
|
||||
### 注意点
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 本课打油诗
|
||||
|
||||
> 自带函数真方便,头文件里全找见。
|
||||
> 省心省力少出错,一行代码就实现。
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
(待补充)
|
||||
1.
|
||||
2.
|
||||
632
第01课-C++自带函数/编程题-教师版.md
Normal file
632
第01课-C++自带函数/编程题-教师版.md
Normal file
@@ -0,0 +1,632 @@
|
||||
# 第1课编程题(教师版)
|
||||
|
||||
## 本课知识点覆盖表
|
||||
|
||||
| 核心知识点 | GESP等级 | 对应课堂题 |
|
||||
|---|---|---|
|
||||
| `sqrt`、`pow`、`abs`及 `<cmath>` | 一级巩固/二级拓展 | 课堂1 |
|
||||
| `max`、`min`、`swap`及 `<algorithm>` | 二级 | 课堂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 <iostream>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
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` 返回浮点数;格式化输出需要包含 `<iomanip>`。
|
||||
|
||||
### 课堂2:整理三个数【适中】
|
||||
|
||||
**题目描述:** 输入三个整数,第一行输出最大值和最小值;再使用 `swap` 将三个变量调整为升序,第二行输出调整结果。
|
||||
|
||||
**输入格式:** 输入三个整数。
|
||||
|
||||
**输出格式:** 第一行输出最大值和最小值;第二行输出升序结果。
|
||||
|
||||
**数据范围:** `-10^9≤a,b,c≤10^9`。
|
||||
|
||||
**输入样例:**
|
||||
```text
|
||||
8 2 5
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```text
|
||||
8 2
|
||||
2 5 8
|
||||
```
|
||||
|
||||
**解题思路:** 用嵌套的 `max`、`min` 求最值;再进行三次相邻比较,需要时调用 `swap`。
|
||||
|
||||
**参考代码:**
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
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 <iostream>
|
||||
#include <algorithm>
|
||||
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 <iostream>
|
||||
#include <cctype>
|
||||
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 <iostream>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
double x, y;
|
||||
cin >> x >> y;
|
||||
cout << fixed << setprecision(2)
|
||||
<< sqrt(pow(x, 2) + pow(y, 2));
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 不要忘记 `<cmath>` 和 `<iomanip>`。
|
||||
|
||||
### 课后2:两数差的绝对值【简单】
|
||||
|
||||
**题目描述:** 输入两个整数,输出它们差的绝对值。
|
||||
|
||||
**输入格式:** 输入两个整数。
|
||||
|
||||
**输出格式:** 输出 `|a-b|`。
|
||||
|
||||
**数据范围:** 两数绝对值不超过 10000。
|
||||
|
||||
**输入样例:**
|
||||
```text
|
||||
3 9
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```text
|
||||
6
|
||||
```
|
||||
|
||||
**解题思路:** 直接调用 `abs(a-b)`。
|
||||
|
||||
**参考代码:**
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
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 <iostream>
|
||||
#include <cctype>
|
||||
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<a≤10000`。
|
||||
|
||||
**输入样例:**
|
||||
```text
|
||||
2
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```text
|
||||
2.828
|
||||
```
|
||||
|
||||
**解题思路:** 对角线为 `sqrt(2)*a`。
|
||||
|
||||
**参考代码:**
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
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 <iostream>
|
||||
#include <algorithm>
|
||||
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 <iostream>
|
||||
#include <algorithm>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
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 <iostream>
|
||||
#include <cmath>
|
||||
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 <iostream>
|
||||
#include <cmath>
|
||||
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 <iostream>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 分类统计必须依据转换前的字符;数字不参与大小写转换。
|
||||
160
第02课-循环结构/教案-学生版.md
Normal file
160
第02课-循环结构/教案-学生版.md
Normal file
@@ -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` 只结束因数检查,不会结束外层枚举。
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
|
||||
> 请完成本课对应的课后作业,见编程题文件。
|
||||
198
第02课-循环结构/教案-教师版.md
Normal file
198
第02课-循环结构/教案-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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.
|
||||
690
第02课-循环结构/编程题-教师版.md
Normal file
690
第02课-循环结构/编程题-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
#include <algorithm>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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<b≤n` 且 `a*b=m` 的数对。
|
||||
|
||||
**输入格式:** 输入枚举上界和目标乘积。
|
||||
|
||||
**输出格式:** 每行一对 `a b`;没有则输出 `NONE`。
|
||||
|
||||
**数据范围:** `1≤n≤1000`,`1≤m≤10^6`。
|
||||
|
||||
**输入样例:**
|
||||
```text
|
||||
10 24
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```text
|
||||
3 8
|
||||
4 6
|
||||
```
|
||||
|
||||
**解题思路:** 外层枚举 `a`,内层只从 `a+1` 枚举 `b`,避免重复和 `a=b`。
|
||||
|
||||
**参考代码:**
|
||||
```cpp
|
||||
#include <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <cmath>
|
||||
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`;并列规则必须按顺序完整处理。
|
||||
151
第03课-自定义函数/教案-学生版.md
Normal file
151
第03课-自定义函数/教案-学生版.md
Normal file
@@ -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.
|
||||
171
第03课-自定义函数/教案-教师版.md
Normal file
171
第03课-自定义函数/教案-教师版.md
Normal file
@@ -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 <iostream>
|
||||
#include <cmath>
|
||||
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.
|
||||
687
第03课-自定义函数/编程题-教师版.md
Normal file
687
第03课-自定义函数/编程题-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 分清每个函数的单一职责;不要在多个位置重复编写同一段逻辑。
|
||||
681
第04课-数据类型与分支结构/分支结构-编程题-教师版.md
Normal file
681
第04课-数据类型与分支结构/分支结构-编程题-教师版.md
Normal file
@@ -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 <iostream>
|
||||
#include <iomanip>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <cmath>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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<w≤100`。
|
||||
|
||||
**输入样例:**
|
||||
```text
|
||||
6.2 1 1
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```text
|
||||
48.00
|
||||
```
|
||||
|
||||
**解题思路:** 先向上取整重量并按档位计算,再依次处理偏远和加急条件。
|
||||
|
||||
**参考代码:**
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 数据检查、特殊规则、普通规则的优先级不能调换。
|
||||
289
第04课-数据类型与分支结构/教案-学生版.md
Normal file
289
第04课-数据类型与分支结构/教案-学生版.md
Normal file
@@ -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
|
||||
```
|
||||
|
||||
**解题思路:** 依次比较距离、分数、编号,三层优先级构成完整更新条件。
|
||||
|
||||
**易错提醒:** 三层规则按先后顺序连接,每个次级规则都只能在前级相等时生效。
|
||||
|
||||
## 课后作业
|
||||
|
||||
> 请完成本课对应的课后作业,见编程题文件。
|
||||
283
第04课-数据类型与分支结构/教案-教师版.md
Normal file
283
第04课-数据类型与分支结构/教案-教师版.md
Normal file
@@ -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 <iostream>
|
||||
#include <cctype> // 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.
|
||||
581
第04课-数据类型与分支结构/数据类型-编程题-教师版.md
Normal file
581
第04课-数据类型与分支结构/数据类型-编程题-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <iomanip>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 计算平移量和应用平移量使用的是同一个循环公式。
|
||||
151
第05课-嵌套循环与图形打印/教案-学生版.md
Normal file
151
第05课-嵌套循环与图形打印/教案-学生版.md
Normal file
@@ -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`。
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
|
||||
> 请完成本课对应的课后作业,见编程题文件。
|
||||
310
第05课-嵌套循环与图形打印/教案-教师版.md
Normal file
310
第05课-嵌套循环与图形打印/教案-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 个乘法式子)
|
||||
660
第05课-嵌套循环与图形打印/编程题-教师版.md
Normal file
660
第05课-嵌套循环与图形打印/编程题-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <cmath>
|
||||
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 <iostream>
|
||||
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` 开始,避免最窄的中间行重复。
|
||||
144
第06课-字符数组与string/教案-学生版.md
Normal file
144
第06课-字符数组与string/教案-学生版.md
Normal file
@@ -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
|
||||
```
|
||||
|
||||
**解题思路:** 先构造一个无空格、全小写的新字符串,再用左右下标向中间比较。
|
||||
|
||||
**易错提醒:** 比较前必须统一大小写;空字符串和单字符都属于回文串。
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
> 请完成本课对应的课后作业,见编程题文件。
|
||||
301
第06课-字符数组与string/教案-教师版.md
Normal file
301
第06课-字符数组与string/教案-教师版.md
Normal file
@@ -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课学过,头文件是 `<cctype>`。
|
||||
|
||||
### 伪代码
|
||||
```
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
#include <cctype> // 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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 类 |
|
||||
|------|-------------------|-----------|
|
||||
| 头文件 | 不需要(或 `<cstring>`) | `<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. 输入一个字符串,将其中的数字提取出来组成新字符串并输出
|
||||
655
第06课-字符数组与string/编程题-教师版.md
Normal file
655
第06课-字符数组与string/编程题-教师版.md
Normal file
@@ -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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
string s;
|
||||
cin >> s;
|
||||
cout << s.size();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 长度是字符个数,最后一个字符的下标是长度减 1。
|
||||
|
||||
### 课后2:统计指定字符【简单】
|
||||
|
||||
**题目描述:** 输入一个不含空格的字符串和一个字符,统计该字符出现次数。
|
||||
|
||||
**输入格式:** 输入一个字符串和一个待统计字符。
|
||||
|
||||
**输出格式:** 指定字符出现次数。
|
||||
|
||||
**数据范围:** 字符串长度不超过 200。
|
||||
|
||||
**输入样例:**
|
||||
```text
|
||||
banana a
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```text
|
||||
3
|
||||
```
|
||||
|
||||
**解题思路:** 遍历字符串,字符相等时计数。
|
||||
|
||||
**参考代码:**
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 只比较字符串长度不够,必须比较每个字母的出现次数。
|
||||
171
第07课-数组计数法/教案-学生版.md
Normal file
171
第07课-数组计数法/教案-学生版.md
Normal file
@@ -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 还原。
|
||||
|
||||
**易错提醒:** 负数不能直接作为数组下标;映射和还原必须使用相同偏移量。
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
|
||||
> 请完成本课对应的课后作业,见编程题文件。
|
||||
283
第07课-数组计数法/教案-教师版.md
Normal file
283
第07课-数组计数法/教案-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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。
|
||||
|
||||
> 提示:先判断题目中的数字范围,再决定计数数组要开多大。
|
||||
721
第07课-数组计数法/编程题-教师版.md
Normal file
721
第07课-数组计数法/编程题-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <cmath>
|
||||
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 <iostream>
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 同一颜色出现三次只能组成一对并剩下一只。
|
||||
145
第08课-枚举与模拟/教案-学生版.md
Normal file
145
第08课-枚举与模拟/教案-学生版.md
Normal file
@@ -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 秒。
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
|
||||
> 请完成本课对应的课后作业,见编程题文件。
|
||||
227
第08课-枚举与模拟/教案-教师版.md
Normal file
227
第08课-枚举与模拟/教案-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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³)
|
||||
676
第08课-枚举与模拟/编程题-教师版.md
Normal file
676
第08课-枚举与模拟/编程题-教师版.md
Normal file
@@ -0,0 +1,676 @@
|
||||
# 第8课编程题(教师版)
|
||||
|
||||
## 本课知识点覆盖表
|
||||
|
||||
| 核心知识点 | 等级定位 | 对应课堂题 |
|
||||
|---|---|---|
|
||||
| 单变量枚举、范围与验证条件 | 三级工具,服务一二级循环分支 | 课堂1 |
|
||||
| 双变量枚举、利用约束缩小范围 | 三级工具,服务简单数学 | 课堂2 |
|
||||
| 状态变量、按顺序更新 | 三级工具,服务循环模拟 | 课堂3 |
|
||||
| 周期过程模拟、取模与分支 | 三级工具,服务综合判断 | 课堂4 |
|
||||
|
||||
## 一、课堂练习(4题)
|
||||
|
||||
### 课堂1:鸡兔同笼【适中】
|
||||
|
||||
**题目描述:** 笼中只有鸡和兔。输入头数与脚数,输出鸡和兔的数量;没有合法方案时输出 `NONE`。
|
||||
|
||||
**输入格式:** 输入动物总数和脚的总数。
|
||||
|
||||
**输出格式:** 输出鸡的数量和兔的数量,或输出 `NONE`。
|
||||
|
||||
**数据范围:** 动物总数为 1~1000,脚数为非负整数。
|
||||
|
||||
**输入样例:**
|
||||
```text
|
||||
10 28
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```text
|
||||
6 4
|
||||
```
|
||||
|
||||
**解题思路:** 枚举鸡的数量,兔的数量由总数减去鸡数得到,再验证脚数。
|
||||
|
||||
**参考代码:**
|
||||
```cpp
|
||||
#include <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <iomanip>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
**易错提醒:** 失败购买不能扣除余额;操作必须严格按输入顺序处理。
|
||||
182
第11课-GESP二级真题训练二/教案-学生版.md
Normal file
182
第11课-GESP二级真题训练二/教案-学生版.md
Normal file
@@ -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`(含空格时)
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
|
||||
> 请完成本课对应的课后作业,见编程题文件。
|
||||
292
第11课-GESP二级真题训练二/教案-教师版.md
Normal file
292
第11课-GESP二级真题训练二/教案-教师版.md
Normal file
@@ -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 <iostream>
|
||||
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 <iostream>
|
||||
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 <iostream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
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` 等函数需要 `<cctype>`**:别忘了加这个头文件。
|
||||
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 分钟检查
|
||||
|
||||
---
|
||||
|
||||
## 常见错误检查清单
|
||||
|
||||
在交卷前逐条检查:
|
||||
|
||||
- [ ] 头文件是否包含(`<iostream>`、`<string>`、`<cmath>`、`<cctype>` 等)
|
||||
- [ ] `using namespace std;` 是否写了
|
||||
- [ ] 变量是否初始化(特别是累加器、计数器)
|
||||
- [ ] 数组下标是否越界(从 0 到 n-1)
|
||||
- [ ] 循环边界是否正确(`<` vs `<=`)
|
||||
- [ ] 输出格式是否正确(空格、换行、中文/英文标点)
|
||||
- [ ] 是否处理了边界情况(0、1、负数等)
|
||||
- [ ] 整数除法是否需要转浮点数
|
||||
- [ ] string 输入是否用了 `getline`(含空格时)
|
||||
|
||||
---
|
||||
|
||||
## 本课打油诗
|
||||
|
||||
> 综合练习查漏洞,各种题型过一遍。
|
||||
> 先审题意后动手,伪代码里思路现。
|
||||
> 边界条件要想全,样例测完再交卷。
|
||||
> 时间分配有策略,不会先跳回头见。
|
||||
|
||||
---
|
||||
|
||||
## 课后作业
|
||||
1. 补完课堂限时练习中未完成的题目
|
||||
2. 整理自己的错题本,写出错误原因和正确解法
|
||||
3. 在洛谷上完成 GESP 一级真题中的 3 道编程题
|
||||
Reference in New Issue
Block a user