Files
project1/第06课-一维数组/编程题-教师版.md

678 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第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;
}
```
**易错提醒:** 排序后只需比较相邻元素;差值相同时不更新即可保留较小的一对。