调整课次结构:前9节重新排序,新建第06课一维数组,后3节不变

This commit is contained in:
chengzi
2026-07-03 15:27:11 +08:00
parent 30f803784a
commit b973fb7825
29 changed files with 1301 additions and 13 deletions

109
CLAUDE.md Normal file
View File

@@ -0,0 +1,109 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
这是一个 **GESP暑假集训教案** 项目,用于少儿编程教学。
- **课程目标**巩固GESP一级内容备战9月等级考试适当铺垫二级内容
- **适合学生**学过一年C++基础不扎实3月一级未通过
- **课时安排**12节课 × 2小时
- **考试目标**9月GESP一级全员通过
## 教案结构
### 文件组织
```
10-教案/
├── GESP暑假集训教案.md # 课程总纲
├── GESP考试大纲整理.md # 考试大纲
├── 第01课-数据类型与分支结构/ # 含教师版+学生版
├── 第02课-循环结构/ # 含教师版+学生版+编程题
├── 第03课-嵌套循环与图形打印/ # 含教师版+学生版+编程题
├── 第04课-C++自带函数与数学处理/ # 含教师版+学生版+编程题
├── 第05课-自定义函数/ # 含教师版+学生版+编程题
├── 第06课-一维数组/ # 含教师版+学生版+编程题
├── 第07课-数组运算/ # 含教师版+学生版+编程题
├── 第08课-字符串与string/ # 含教师版+学生版+编程题
├── 第09课-枚举模拟/ # 含教师版+学生版+编程题
├── 第10课-GESP二级真题训练/ # 含教师版+学生版
└── docs/ # 旧版教案归档
```
### 课程大纲
| 节次 | 内容 | 教学目标 |
|------|------|----------|
| 1 | 数据类型与分支结构 | int/char/double/bool/string + if/else + 打擂台 |
| 2 | 循环结构 | for、while、数位拆解、倒序、回文数 |
| 3 | 嵌套循环与图形打印 | 外层管行、内层管列、图形规律 |
| 4 | C++自带函数与数学处理 | sqrt、abs、pow、max/min、swap、sort + 字符判断 |
| 5 | 自定义函数 | 定义/调用/声明、参数传递、返回值、函数重载 |
| 6 | 一维数组 | 遍历、查找、统计、逆序、冒泡排序 |
| 7 | 数组运算 | 频次统计、最高频查找、去重升序输出 |
| 8 | 字符串与 string | 遍历、统计、查找、回文判断 |
| 9 | 枚举模拟 | 枚举范围、条件判断、过程模拟 |
| 10 | GESP一级真题训练 | 一级真题实战,查漏补缺 |
| 11 | GESP二级真题训练一 | 二级真题实战 |
| 12 | GESP二级真题训练二 | 二级真题实战,冲刺备考 |
## 教案内容规范
### 每节课教案包含
1. **教学目标** - 明确的学习目标
2. **知识点讲解** - 带代码示例的详细讲解
3. **打油诗口诀** - 帮助学生记忆的韵律口诀
4. **易错点提醒** - 常见错误和注意事项
5. **练习题** - 带参考代码的练习题目
6. **课后作业** - 巩固练习
### 打油诗口诀特点
教案采用打油诗帮助学生记忆算法和知识点,例如:
**打擂台(求最大值)**
> 擂台之上谁最强,先请一位站中央
> 逐一比较谁更大,赢者留下当大王
**累加求和**
> 一个一个往里加,循环转完和到家
> 初始清零别忘记,累加器里答案大
## 编辑指南
### 添加新课程
1. 创建新文件夹:`第XX课-课程名称/`
2. 在文件夹内创建 `教案.md` 文件
3. 遵循现有教案的格式和结构
4. 更新 `GESP暑假集训教案.md` 中的课程大纲
### 修改教案内容
- 保持代码示例的正确性和可运行性
- 确保打油诗押韵且易于记忆
- 练习题难度适中,符合学生水平
- 参考代码要简洁清晰
### 风格指南
- 使用中文编写所有面向学生的内容
- 代码标识符和关键字使用英文
- 保持一致的格式和排版
- 每节课的教案长度适中,便于课堂使用
## GESP考试信息
- **官方网站**https://gesp.ccf.org.cn
- **刷题平台**https://www.luogu.com.cn洛谷
- **考试时长**120分钟
- **满分**100分
- **合格线**60分
## 参考资源
- [GESP考试大纲整理.md](./GESP考试大纲整理.md) - 详细的考试大纲和知识点
- [GESP暑假集训教案.md](./GESP暑假集训教案.md) - 课程总纲和打油诗汇总

View File

@@ -27,18 +27,18 @@
| 节次 | 内容 |
|------|------|
| **1** | **C++自带函数**sqrt、abs、pow、max/min、swap、sort + 字符判断函数 |
| **2** | **自定义函数**定义/调用/声明、参数传递、返回值、函数重载、局部/全局变量 |
| **3** | **数据类型**int、char、double、bool、string+ ASCII码 |
| **4** | **分支结构**if、else if、else+ 打擂台(含二次打擂台 |
| **5** | **循环**for、while、数位拆解、回文数、水仙花数 |
| **6** | **嵌套循环与图形打印**(外层管行、内层管列、图形规律 |
| **7** | **数组基础**遍历、查找、统计、逆序、冒泡排序 |
| **8** | **字符数组与 string**(遍历、统计、查找、回文判断) |
| **9** | **数组计数法**频次统计、最高频查找、去重升序输出 |
| **10** | **枚举与模拟**(枚举范围、条件判断、过程模拟) |
| 11 | 综合练习 |
| 12 | 模拟考试 + 查漏补缺 |
| **1** | **数据类型与分支结构**int、char、double、bool、string + if/else + 打擂台 |
| **2** | **循环结构**for、while、数位拆解、倒序、回文数 |
| **3** | **嵌套循环与图形打印**(外层管行、内层管列、图形规律) |
| **4** | **C++自带函数与数学处理**sqrt、abs、pow、max/min、swap、sort + 字符判断函数 |
| **5** | **自定义函数**定义/调用/声明、参数传递、返回值、函数重载、局部/全局变量 |
| **6** | **一维数组**(遍历、查找、统计、逆序、冒泡排序 |
| **7** | **数组运算**频次统计、最高频查找、去重升序输出 |
| **8** | **字符与 string**(遍历、统计、查找、回文判断) |
| **9** | **枚举模拟**枚举范围、条件判断、过程模拟 |
| 10 | GESP一级真题训练 |
| 11 | GESP二级真题训练一 |
| 12 | GESP二级真题训练二 |
> 每节课含 2-3 道经典题目,其中 1 道由橙子(我)完整编写,其余由橙子自选补充。
@@ -47,9 +47,53 @@
每节课一个文件夹,内含教师版和学生版两个文件:
```
第01课-C++自带函数/
第01课-数据类型与分支结构/
├── 教案-教师版.md ← 完整内容
└── 教案-学生版.md ← 关键内容留空(学生填写)
第02课-循环结构/
├── 教案-教师版.md
├── 教案-学生版.md
└── 编程题-教师版.md
第03课-嵌套循环与图形打印/
├── 教案-教师版.md
├── 教案-学生版.md
└── 编程题-教师版.md
第04课-C++自带函数与数学处理/
├── 教案-教师版.md
├── 教案-学生版.md
└── 编程题-教师版.md
第05课-自定义函数/
├── 教案-教师版.md
├── 教案-学生版.md
└── 编程题-教师版.md
第06课-一维数组/
├── 教案-教师版.md
├── 教案-学生版.md
└── 编程题-教师版.md
第07课-数组运算/
├── 教案-教师版.md
├── 教案-学生版.md
└── 编程题-教师版.md
第08课-字符串与string/
├── 教案-教师版.md
├── 教案-学生版.md
└── 编程题-教师版.md
第09课-枚举模拟/
├── 教案-教师版.md
├── 教案-学生版.md
└── 编程题-教师版.md
第10课-GESP二级真题训练/
├── 教案-教师版.md
└── 教案-学生版.md
```
### 教师版 vs 学生版

View File

@@ -0,0 +1,158 @@
# 第6课一维数组
## 教学目标
- 掌握一维数组的定义、初始化和访问
- 能使用数组进行遍历、查找、统计等基本操作
- 理解数组逆序和冒泡排序的原理
## 核心考点:数组下标、遍历与越界
### 考什么
使用数组完成读入、统计、查找和排序并判断每个元素对应的________。
### 必须理解
长度为 n 的数组合法下标从________到________。`arr[i]` 表示第________个元素。访问 `arr[n]` 会发生________。
### 核心写法
```cpp
for (int i = ________; i < ________; i++) cin >> arr[i];
int maximum = ________;
for (int i = ________; i < n; i++) {
if (arr[i] > maximum) ____________________;
}
```
### 常见错误
- `i <= n` 为什么会越界________________
- 全是负数时最大值能否初始化为0________________
- "第5个元素"的下标是多少________
### 判断是否掌握
- [ ] 我能说出数组的首尾合法下标。
- [ ] 我能写出无越界的遍历循环。
- [ ] 我能用数组完成查找和打擂台。
---
## 题目一:数组入门 — 求最大值与平均值
### 核心代码(一句话)
> 数组遍历的核心:**`______`**,下标从 ______ 开始,到 ______ 结束;打擂台求最值,先请 ______ 当擂主。
### 自然语言思路
### 伪代码
```
1. 输入 n
2. 定义数组 ______
3. for i = 0 到 ______
4. 输入 ______ // 读入数组
5. maxVal = ______ // 擂主
6. sum = ______
7. for i = ______ 到 ______
8. if arr[i] ______ maxVal // 打擂
9. maxVal = ______
10. sum += ______ // 累加
11. avg = ________________ // 整数转浮点数
12. 输出 maxVal 和 avg
```
### 真实代码
```cpp
```
### 注意点
---
## 题目二:查找元素 — 找目标值
### 核心代码(一句话)
> 查找的核心:**遍历 + 比较 + ______**,找到了就记录位置并退出,用一个 ______ 表示"没找到"。
### 自然语言思路
### 伪代码
```
1. 输入 n, 数组 arr, target
2. index = ______ // 哨兵:默认没找到
3. for i = 0 到 ______
4. if arr[i] ______ target
5. index = ______
6. ______ // 找到就停
7. if index ______ -1
8. 输出 "找到,下标:" + index
9. else
10. 输出 "未找到"
```
### 真实代码
```cpp
```
### 注意点
---
## 题目三:数组逆序与冒泡排序
### 核心代码(一句话)
> 逆序的核心:**首尾交换,只换 ______**;冒泡排序的核心:**相邻两两比较______**,每轮确定一个最大值的位置。
### 自然语言思路
### 伪代码
**数组逆序**
```
1. for i = 0 到 ______
2. temp = ______
3. arr[i] = ______
4. arr[______] = temp
```
**冒泡排序**
```
1. for i = 0 到 ______ // 外层:第 i 轮
2. for j = 0 到 ______ // 内层:相邻比较
3. if arr[j] ______ arr[j+1]// 前面大就交换
4. temp = ______
5. arr[j] = ______
6. arr[j+1] = ______
```
### 真实代码
```cpp
```
### 注意点
---
## 本课打油诗
>
---
## 课后作业
1. 输入 n 个数,求第二大值(注意可能有重复最大值)
2. 输入 n 个数,将它们逆序存储后输出(不能用第二个数组)
3. 输入 n 个数,统计其中不同数字的个数
4. 输入 n 个数,使用冒泡排序从小到大排序后输出

View File

@@ -0,0 +1,300 @@
# 第6课一维数组
## 教学目标
- 掌握一维数组的定义、初始化和访问
- 能使用数组进行遍历、查找、统计等基本操作
- 理解数组逆序和冒泡排序的原理
## 核心考点:数组下标、遍历与越界
### 考什么
考试常要求读入一组数据并完成求和、最值、查找、逆序或排序,也会直接考某个下标对应哪个元素。所有数组题的基础都是正确理解下标范围并完整遍历。
### 必须理解
长度为 n 的数组包含 n 个元素,但合法下标是 `0~n-1``arr[i]` 表示第 i+1 个元素,而不是第 i 个元素。遍历通常写成 `for (int i=0; i<n; i++)`,这样第一个和最后一个元素都恰好访问一次。数组不会自动检查越界;访问 `arr[n]` 已经超出范围,可能读到垃圾值或破坏其他数据。打擂台求最值时,最安全的初值通常是 `arr[0]`再从下标1开始比较。
### 核心写法
```cpp
int arr[100];
for (int i = 0; i < n; i++) cin >> arr[i];
int maximum = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > maximum) maximum = arr[i];
}
```
数组容量要不小于 n循环条件必须是 `i < n`
### 常见错误
- 写成 `i <= n`,最后访问到不存在的 `arr[n]`
- 把"第几个元素"和"下标是多少"混为一谈。
- 最大值初始化为0遇到全负数数组时得到错误答案。
- 使用未初始化的数组元素参与计算。
### 判断是否掌握
给定数组长度 n能立即说出首尾下标能写出无越界的输入和遍历循环并能正确完成查找、统计或打擂台。
---
## 题目一:数组入门 — 求最大值与平均值
### 核心代码(一句话)
> 数组遍历的核心:**`for (i = 0; i < n; i++)`**,下标从 0 开始,到 n-1 结束;打擂台求最值,先请第一个当擂主。
### 自然语言思路
老师手上有 n 个分数,要找出最高分和平均分。
第一步,把 n 个分数先存进数组 `arr[0]``arr[n-1]`,用 `cin` 配合 `for` 循环读入。
第二步,求最大值用"打擂台":先假设 `arr[0]` 是最大的(擂主),然后从 `arr[1]` 开始逐个挑战,谁更大谁就当新擂主。
第三步,求平均值:先把所有元素加到一起(累加器初始为 0循环一遍累加最后除以 n。
第四步,注意平均值可能是小数,要用 `double` 类型,整数除整数会丢掉小数部分。
### 伪代码
```
1. 输入 n
2. 定义数组 arr[n]
3. for i = 0 到 n-1
4. 输入 arr[i] // 读入数组
5. maxVal = arr[0] // 擂主
6. sum = 0
7. for i = 0 到 n-1
8. if arr[i] > maxVal // 打擂
9. maxVal = arr[i]
10. sum += arr[i] // 累加
11. avg = (double)sum / n // 整数转浮点数
12. 输出 maxVal 和 avg
```
### 真实代码
```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];
for (int i = 1; i < n; i++) {
if (arr[i] > maxVal) {
maxVal = arr[i];
}
}
// 累加求和
int sum = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
}
double avg = (double)sum / n; // 强转,保留小数
cout << "最大值:" << maxVal << endl;
cout << "平均值:" << avg << endl;
return 0;
}
```
### 注意点
1. **下标从 0 开始**`arr[0]` 是第一个元素,`arr[n-1]` 是最后一个。这是新手最容易犯的错。
2. **打擂台循环从 1 开始**`for (i = 1; i < n; i++)`,因为 `arr[0]` 已经是擂主了,不用跟自己比。
3. **整数除法陷阱**`sum / n` 两个 int 相除会丢掉小数,必须把其中一个转成 `double`
4. **数组大小**`int arr[n]` 在部分老编译器(如 VC6上不支持变长数组可以用 `int arr[1000]` 设一个大一点的固定值。
---
## 题目二:查找元素 — 找目标值
### 核心代码(一句话)
> 查找的核心:**遍历 + 比较 + break**,找到了就记录位置并退出,用一个 -1 表示"没找到"。
### 自然语言思路
在一个数组里找某个数,就像在一排抽屉里找东西。
第一步,设一个标记 `index = -1`,表示"还没找到"-1 不可能是合法下标,所以用它当哨兵)。
第二步,从第一个到最后一个,逐个打开抽屉 `arr[i]`,跟目标值比。
第三步,一旦找到,记录下标 `index = i`,立刻 `break` 退出循环,不浪费时间。
第四步,循环结束后,如果 `index` 还是 -1 就是没找到,否则输出下标。
> 也叫"顺序查找"或"线性查找",像沿着一条线逐个摸过去。
### 伪代码
```
1. 输入 n, 数组 arr, target
2. index = -1 // 哨兵:默认没找到
3. for i = 0 到 n-1
4. if arr[i] == target
5. index = i
6. break // 找到就停
7. if index != -1
8. 输出 "找到,下标:" + index
9. else
10. 输出 "未找到"
```
### 真实代码
```cpp
#include <iostream>
using namespace std;
int main() {
int n, target;
cin >> n;
int arr[n];
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
cin >> target;
int index = -1; // 哨兵
for (int i = 0; i < n; i++) {
if (arr[i] == target) {
index = i;
break; // 找到就停
}
}
if (index != -1) {
cout << "找到,下标为:" << index << endl;
} else {
cout << "未找到" << endl;
}
return 0;
}
```
### 注意点
1. **哨兵值选 -1**:因为数组下标从 0 开始,-1 永远不可能是合法下标,所以用它表示"没找到"很安全。
2. **break 的作用**:找到后立刻停止,不需要继续往后找了。如果忘了加 `break`,会找到最后一次出现的位置。
3. **如果找第一个出现的位置**:加 `break`**如果找最后一次出现的位置**:不加 `break`,让循环跑完。
---
## 题目三:数组逆序与冒泡排序
### 核心代码(一句话)
> 逆序的核心:**首尾交换,只换一半**;冒泡排序的核心:**相邻两两比较,大的往后冒**,每轮确定一个最大值的位置。
### 自然语言思路
**数组逆序**
第一步,想象把数组对折:第 0 个和第 n-1 个交换,第 1 个和第 n-2 个交换……
第二步,循环只需要跑 `n / 2` 次,因为每次交换两个元素。
第三步,交换用三行代码(中间变量法):`temp = a; a = b; b = temp;`
**冒泡排序**
第一步,外层循环决定"第几轮",一共需要 n-1 轮。
第二步,每轮从数组开头开始,相邻两个比大小:如果前面的 > 后面的,就交换。
第三步,每轮结束,最大的那个数就"冒"到了最后面。所以下一轮可以少比一个(`j < n-1-i`)。
第四步n-1 轮过后,数组从小到大排好了。
### 伪代码
**数组逆序**
```
1. for i = 0 到 n/2 - 1
2. temp = arr[i]
3. arr[i] = arr[n-1-i]
4. arr[n-1-i] = temp
```
**冒泡排序**
```
1. for i = 0 到 n-2 // 外层:第 i 轮
2. for j = 0 到 n-2-i // 内层:相邻比较
3. if arr[j] > arr[j+1] // 前面大就交换
4. temp = arr[j]
5. arr[j] = arr[j+1]
6. arr[j+1] = temp
```
### 真实代码
**数组逆序**
```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];
}
// 首尾交换,只换一半
for (int i = 0; i < n / 2; i++) {
int temp = arr[i];
arr[i] = arr[n - 1 - i];
arr[n - 1 - i] = temp;
}
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
```
**冒泡排序**
```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];
}
// 冒泡排序
for (int i = 0; i < n - 1; i++) { // 外层n-1 轮
for (int j = 0; j < n - 1 - i; j++) { // 内层:每轮少比一个
if (arr[j] > arr[j + 1]) { // 前面大则交换
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
```
### 注意点
1. **逆序循环次数**`n / 2`,不是 `n`。如果是奇数个元素,中间那个不用换。例如 n=5只需换 2 次(下标 0↔41↔3
2. **冒泡内层循环上界**`n - 1 - i`,不是 `n - 1`。每轮少比一个是因为最后面已经排好了。
3. **冒泡时间复杂度**n 个数大概要比 n² / 2 次,数据量大时较慢。学完 sort 后可以直接用 `sort` 替代冒泡。
4. **交换三行口诀**`temp = 前; 前 = 后; 后 = temp;` —— 中间变量暂存,避免覆盖丢失。
---
## 本课打油诗
> 数组元素排排坐,下标从零开始摸。
> 遍历循环 for 一把,一个不落全看过。
> 擂台之上比高矮,先请一位站中央。
> 首尾交换逆序成,冒泡大的往后挪。
---
## 课后作业
1. 输入 n 个数,求第二大值(注意可能有重复最大值)
2. 输入 n 个数,将它们逆序存储后输出(不能用第二个数组)
3. 输入 n 个数,统计其中不同数字的个数
4. 输入 n 个数,使用冒泡排序从小到大排序后输出

View File

@@ -0,0 +1,677 @@
# 第6课编程题教师版
## 本课知识点覆盖表
| 核心知识点 | GESP等级 | 对应课堂题 |
|---|---|---|
| 数组定义、初始化、合法下标、遍历与平均值 | 三级工具 | 课堂1 |
| 顺序查找、首次与末次位置、哨兵值 | 三级工具 | 课堂2 |
| 分类统计、打擂台求最值及下标 | 三级工具 | 课堂3 |
| 原地逆序、直接调用 `sort` 完成升序 | 三级工具 | 课堂4 |
## 一、课堂练习4题
### 课堂1成绩分析报告【适中】
**题目描述:** 输入若干成绩,输出总分、平均分、最高分和最低分。平均分保留两位小数。
**输入格式:** 第一行输入学生人数;第二行输入每名学生的成绩。
**输出格式:** 依次输出总分、平均分、最高分和最低分。
**数据范围:** 学生人数为 1100成绩为 0100。
**输入样例:**
```text
5
80 95 70 85 90
```
**输出样例:**
```text
420 84.00 95 70
```
**解题思路:** 用数组保存数据并完整遍历;总分从 0 开始,最值用第一个元素初始化。
**参考代码:**
```cpp
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int n, a[105], sum = 0;
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
int biggest = a[0], smallest = a[0];
for (int i = 0; i < n; i++) {
sum += a[i];
if (a[i] > biggest) biggest = a[i];
if (a[i] < smallest) smallest = a[i];
}
cout << sum << " " << fixed << setprecision(2)
<< 1.0 * sum / n << " " << biggest << " " << smallest;
return 0;
}
```
**易错提醒:** 长度为学生人数的数组,最后一个合法下标是人数减 1。
### 课堂2目标值的位置【适中】
**题目描述:** 输入一个数组和目标值,输出目标值第一次出现的位置、最后一次出现的位置和出现次数。位置从 1 开始;没有出现时输出 `-1 -1 0`
**输入格式:** 第一行输入数组长度和目标值;第二行输入数组中的所有整数。
**输出格式:** 第一次位置、最后一次位置和出现次数。
**数据范围:** 数组长度为 11000元素绝对值不超过一万。
**输入样例:**
```text
7 5
2 5 3 5 5 8 1
```
**输出样例:**
```text
2 5 3
```
**解题思路:** 第一次位置初始化为 -1遍历时首次命中记录第一次位置每次命中都更新最后位置并计数。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, target, a[1005];
cin >> n >> target;
for (int i = 0; i < n; i++) cin >> a[i];
int first = -1, last = -1, count = 0;
for (int i = 0; i < n; i++) {
if (a[i] == target) {
if (first == -1) first = i + 1;
last = i + 1;
count++;
}
}
cout << first << " " << last << " " << count;
return 0;
}
```
**易错提醒:** 数组下标从 0 开始,题目位置从 1 开始,输出时需要加 1。
### 课堂3最优秀的进步者【适中】
**题目描述:** 输入每名学生上次和本次成绩,输出进步分数最大的学生编号;若并列,选择本次成绩更高者;仍并列选择编号更小者。
**输入格式:** 第一行输入学生人数;第二行输入上次成绩;第三行输入本次成绩。
**输出格式:** 学生编号和进步分数。
**数据范围:** 学生人数为 1100成绩为 0100。
**输入样例:**
```text
4
60 70 80 50
80 85 90 70
```
**输出样例:**
```text
1 20
```
**解题思路:** 两个数组保存两次成绩;用第一名学生初始化擂主,再按进步值、本次成绩、编号依次比较。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, oldScore[105], newScore[105];
cin >> n;
for (int i = 0; i < n; i++) cin >> oldScore[i];
for (int i = 0; i < n; i++) cin >> newScore[i];
int best = 0;
for (int i = 1; i < n; i++) {
int progress = newScore[i] - oldScore[i];
int bestProgress = newScore[best] - oldScore[best];
if (progress > bestProgress ||
(progress == bestProgress && newScore[i] > newScore[best])) {
best = i;
}
}
cout << best + 1 << " " << newScore[best] - oldScore[best];
return 0;
}
```
**易错提醒:** 并列规则中编号更小者优先,因此前两项相同就不更新。
### 课堂4逆序后升序排列【适中】
**题目描述:** 输入一个数组,先在原数组中首尾交换完成逆序并输出;再直接调用 `sort` 将它升序排列并输出。
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
**输出格式:** 第一行输出逆序数组;第二行输出升序数组。
**数据范围:** 数组长度为 1100元素绝对值不超过一万。
**输入样例:**
```text
5
4 1 3 5 2
```
**输出样例:**
```text
2 5 3 1 4
1 2 3 4 5
```
**解题思路:** 逆序只交换一半;需要排序时直接调用 `sort`,本课不学习手写排序算法。
**参考代码:**
```cpp
#include <iostream>
#include <algorithm>
using namespace std;
void printArray(int a[], int n) {
for (int i = 0; i < n; i++) {
if (i) cout << " ";
cout << a[i];
}
cout << "\n";
}
int main() {
int n, a[105];
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n / 2; i++) {
int temp = a[i];
a[i] = a[n - 1 - i];
a[n - 1 - i] = temp;
}
printArray(a, n);
sort(a, a + n);
printArray(a, n);
return 0;
}
```
**易错提醒:** 逆序下标配对为当前位置与"长度减 1 再减当前位置"`sort` 的结束位置写数组名加长度。
## 二、课后作业3题
### 课后1数组求和【简单】
**题目描述:** 输入一个整数数组,输出所有元素之和。
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
**输出格式:** 所有元素之和。
**数据范围:** 数组长度为 11000元素绝对值不超过一万。
**输入样例:**
```text
4
3 6 2 9
```
**输出样例:**
```text
20
```
**解题思路:** 遍历数组并累加。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, a[1005];
long long sum = 0;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
sum += a[i];
}
cout << sum;
return 0;
}
```
**易错提醒:** 元素之和可能较大,累加器使用 `long long`
### 课后2统计正负零【简单】
**题目描述:** 输入一个数组,分别统计正数、负数和零的个数。
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
**输出格式:** 依次输出正数、负数和零的个数。
**数据范围:** 数组长度为 11000。
**输入样例:**
```text
6
-2 0 5 7 0 -1
```
**输出样例:**
```text
2 2 2
```
**解题思路:** 遍历时使用三分支更新三个计数器。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, a[1005], positive = 0, negative = 0, zero = 0;
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) {
if (a[i] > 0) positive++;
else if (a[i] < 0) negative++;
else zero++;
}
cout << positive << " " << negative << " " << zero;
return 0;
}
```
**易错提醒:** 三种情况互斥,应使用 `if-else if-else`
### 课后3原地逆序【简单】
**题目描述:** 不使用第二个数组,将输入数组逆序后输出。
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
**输出格式:** 逆序后的数组。
**数据范围:** 数组长度为 1100。
**输入样例:**
```text
4
10 20 30 40
```
**输出样例:**
```text
40 30 20 10
```
**解题思路:** 交换首尾对应元素,只需循环数组长度的一半。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, a[105];
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n / 2; i++) {
int temp = a[i];
a[i] = a[n - 1 - i];
a[n - 1 - i] = temp;
}
for (int i = 0; i < n; i++) {
if (i) cout << " ";
cout << a[i];
}
return 0;
}
```
**易错提醒:** 若交换整个数组长度,会把已经交换好的元素再换回来。
## 三、扩展练习7题
### 扩展1循环左移一位【基础综合】
**题目描述:** 将数组所有元素循环左移一位,第一个元素移到最后。
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
**输出格式:** 移动后的数组。
**数据范围:** 数组长度为 1100。
**输入样例:**
```text
5
1 2 3 4 5
```
**输出样例:**
```text
2 3 4 5 1
```
**解题思路:** 暂存第一个元素,依次用后一个元素覆盖前一个位置,最后放回暂存值。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, a[105];
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
int first = a[0];
for (int i = 0; i < n - 1; i++) a[i] = a[i + 1];
a[n - 1] = first;
for (int i = 0; i < n; i++) {
if (i) cout << " ";
cout << a[i];
}
return 0;
}
```
**易错提醒:** 必须先保存第一个元素,否则移动后会丢失。
### 扩展2第二大不同值【中等】
**题目描述:** 输出数组中第二大的不同数;若不存在两个不同数,输出 `NONE`
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
**输出格式:** 第二大的不同数或 `NONE`
**数据范围:** 数组长度为 1100元素绝对值不超过一万。
**输入样例:**
```text
6
5 8 8 3 5 7
```
**输出样例:**
```text
7
```
**解题思路:** 先调用 `sort` 升序,再从倒数第二个位置向前寻找第一个小于最大值的元素。
**参考代码:**
```cpp
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n, a[105];
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
sort(a, a + n);
int index = n - 2;
while (index >= 0 && a[index] == a[n - 1]) index--;
if (index < 0) cout << "NONE";
else cout << a[index];
return 0;
}
```
**易错提醒:** "第二大"指第二大的不同值,重复最大值不能算作答案。
### 扩展3指定位置插入【中等】
**题目描述:** 在数组的指定位置插入一个整数,原位置及后续元素依次后移。位置从 1 开始。
**输入格式:** 第一行输入原数组长度;第二行输入数组元素;第三行输入插入位置和插入值。
**输出格式:** 插入后的数组。
**数据范围:** 原数组长度为 199插入位置合法。
**输入样例:**
```text
4
10 20 30 40
3 25
```
**输出样例:**
```text
10 20 25 30 40
```
**解题思路:** 从数组末尾向插入位置倒序移动,避免覆盖尚未搬走的元素。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, a[105], position, value;
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
cin >> position >> value;
int index = position - 1;
for (int i = n; i > index; i--) a[i] = a[i - 1];
a[index] = value;
n++;
for (int i = 0; i < n; i++) {
if (i) cout << " ";
cout << a[i];
}
return 0;
}
```
**易错提醒:** 插入时必须从后向前移动;从前向后会反复覆盖数据。
### 扩展4删除所有目标值【中等】
**题目描述:** 删除数组中所有等于目标值的元素,并保持其余元素原有顺序。
**输入格式:** 第一行输入数组长度和目标值;第二行输入数组元素。
**输出格式:** 删除后的数组;若数组为空,输出 `EMPTY`
**数据范围:** 数组长度为 1100。
**输入样例:**
```text
7 3
1 3 2 3 3 5 4
```
**输出样例:**
```text
1 2 5 4
```
**解题思路:** 用写入位置记录保留下来的元素个数,遇到非目标值就写到数组前部。
**参考代码:**
```cpp
#include <iostream>
using namespace std;
int main() {
int n, target, a[105], length = 0;
cin >> n >> target;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) {
if (a[i] != target) a[length++] = a[i];
}
if (length == 0) cout << "EMPTY";
else {
for (int i = 0; i < length; i++) {
if (i) cout << " ";
cout << a[i];
}
}
return 0;
}
```
**易错提醒:** 新长度与原数组下标含义不同,不要遗漏最后得到的新长度。
### 扩展5排序后去重【中等】
**题目描述:** 输入一个整数数组,直接调用 `sort` 升序排列,然后只保留每个数的第一次出现,输出去重后的结果。
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
**输出格式:** 第一行输出去重后的元素个数;第二行按升序输出去重结果。
**数据范围:** 数组长度为 11000元素绝对值不超过一万。
**输入样例:**
```text
7
3 1 3 2 1 5 2
```
**输出样例:**
```text
4
1 2 3 5
```
**解题思路:** 排序后相同元素会相邻;第一个元素直接保留,后续元素与上一个元素不同才写入数组前部。
**参考代码:**
```cpp
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n, a[1005];
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
sort(a, a + n);
int length = 1;
for (int i = 1; i < n; i++) {
if (a[i] != a[length - 1]) a[length++] = a[i];
}
cout << length << "\n";
for (int i = 0; i < length; i++) {
if (i) cout << " ";
cout << a[i];
}
return 0;
}
```
**易错提醒:** 先排序才能通过比较相邻元素去重;新长度初始为 1。
### 扩展6中位数【略难】
**题目描述:** 输入一个整数数组,直接调用 `sort` 后求中位数。元素个数为奇数时取中间值;为偶数时取中间两个数的平均值,保留一位小数。
**输入格式:** 第一行输入数组长度;第二行输入数组元素。
**输出格式:** 中位数,保留一位小数。
**数据范围:** 数组长度为 1200元素绝对值不超过一万。
**输入样例:**
```text
6
7 1 9 3 5 11
```
**输出样例:**
```text
6.0
```
**解题思路:** 先升序排序;奇数长度访问中间下标,偶数长度计算两个中间元素平均值。
**参考代码:**
```cpp
#include <iostream>
#include <iomanip>
#include <algorithm>
using namespace std;
int main() {
int n, a[205];
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
sort(a, a + n);
double median;
if (n % 2 == 1) median = a[n / 2];
else median = (a[n / 2 - 1] + a[n / 2]) / 2.0;
cout << fixed << setprecision(1) << median;
return 0;
}
```
**易错提醒:** 偶数长度的两个中间下标是"长度除以 2 减 1"和"长度除以 2"。
### 扩展7最小差值数对【略难】
**题目描述:** 输入互不相同的整数,找出差的绝对值最小的两个数,按从小到大输出;若多对差值相同,输出较小数字更小的一对。
**输入格式:** 第一行输入数组长度;第二行输入所有互不相同的整数。
**输出格式:** 满足条件的两个数和它们的差值。
**数据范围:** 数组长度为 2200元素绝对值不超过一万。
**输入样例:**
```text
5
8 1 15 5 3
```
**输出样例:**
```text
1 3 2
```
**解题思路:** 先调用 `sort` 升序;最小差值一定出现在相邻元素之间,再用打擂台处理并列规则。
**参考代码:**
```cpp
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n, a[205];
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
sort(a, a + n);
int best = 0;
for (int i = 1; i < n - 1; i++) {
if (a[i + 1] - a[i] < a[best + 1] - a[best])
best = i;
}
cout << a[best] << " " << a[best + 1] << " "
<< a[best + 1] - a[best];
return 0;
}
```
**易错提醒:** 排序后只需比较相邻元素;差值相同时不更新即可保留较小的一对。