678 lines
16 KiB
Markdown
678 lines
16 KiB
Markdown
# 第6课编程题(教师版)
|
||
|
||
## 本课知识点覆盖表
|
||
|
||
| 核心知识点 | GESP等级 | 对应课堂题 |
|
||
|---|---|---|
|
||
| 数组定义、初始化、合法下标、遍历与平均值 | 三级工具 | 课堂1 |
|
||
| 顺序查找、首次与末次位置、哨兵值 | 三级工具 | 课堂2 |
|
||
| 分类统计、打擂台求最值及下标 | 三级工具 | 课堂3 |
|
||
| 原地逆序、直接调用 `sort` 完成升序 | 三级工具 | 课堂4 |
|
||
|
||
## 一、课堂练习(4题)
|
||
|
||
### 课堂1:成绩分析报告【适中】
|
||
|
||
**题目描述:** 输入若干成绩,输出总分、平均分、最高分和最低分。平均分保留两位小数。
|
||
|
||
**输入格式:** 第一行输入学生人数;第二行输入每名学生的成绩。
|
||
|
||
**输出格式:** 依次输出总分、平均分、最高分和最低分。
|
||
|
||
**数据范围:** 学生人数为 1~100,成绩为 0~100。
|
||
|
||
**输入样例:**
|
||
```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`。
|
||
|
||
**输入格式:** 第一行输入数组长度和目标值;第二行输入数组中的所有整数。
|
||
|
||
**输出格式:** 第一次位置、最后一次位置和出现次数。
|
||
|
||
**数据范围:** 数组长度为 1~1000,元素绝对值不超过一万。
|
||
|
||
**输入样例:**
|
||
```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:最优秀的进步者【适中】
|
||
|
||
**题目描述:** 输入每名学生上次和本次成绩,输出进步分数最大的学生编号;若并列,选择本次成绩更高者;仍并列选择编号更小者。
|
||
|
||
**输入格式:** 第一行输入学生人数;第二行输入上次成绩;第三行输入本次成绩。
|
||
|
||
**输出格式:** 学生编号和进步分数。
|
||
|
||
**数据范围:** 学生人数为 1~100,成绩为 0~100。
|
||
|
||
**输入样例:**
|
||
```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` 将它升序排列并输出。
|
||
|
||
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
|
||
|
||
**输出格式:** 第一行输出逆序数组;第二行输出升序数组。
|
||
|
||
**数据范围:** 数组长度为 1~100,元素绝对值不超过一万。
|
||
|
||
**输入样例:**
|
||
```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:数组求和【简单】
|
||
|
||
**题目描述:** 输入一个整数数组,输出所有元素之和。
|
||
|
||
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
|
||
|
||
**输出格式:** 所有元素之和。
|
||
|
||
**数据范围:** 数组长度为 1~1000,元素绝对值不超过一万。
|
||
|
||
**输入样例:**
|
||
```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:统计正负零【简单】
|
||
|
||
**题目描述:** 输入一个数组,分别统计正数、负数和零的个数。
|
||
|
||
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
|
||
|
||
**输出格式:** 依次输出正数、负数和零的个数。
|
||
|
||
**数据范围:** 数组长度为 1~1000。
|
||
|
||
**输入样例:**
|
||
```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:原地逆序【简单】
|
||
|
||
**题目描述:** 不使用第二个数组,将输入数组逆序后输出。
|
||
|
||
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
|
||
|
||
**输出格式:** 逆序后的数组。
|
||
|
||
**数据范围:** 数组长度为 1~100。
|
||
|
||
**输入样例:**
|
||
```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:循环左移一位【基础综合】
|
||
|
||
**题目描述:** 将数组所有元素循环左移一位,第一个元素移到最后。
|
||
|
||
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
|
||
|
||
**输出格式:** 移动后的数组。
|
||
|
||
**数据范围:** 数组长度为 1~100。
|
||
|
||
**输入样例:**
|
||
```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`。
|
||
|
||
**数据范围:** 数组长度为 1~100,元素绝对值不超过一万。
|
||
|
||
**输入样例:**
|
||
```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 开始。
|
||
|
||
**输入格式:** 第一行输入原数组长度;第二行输入数组元素;第三行输入插入位置和插入值。
|
||
|
||
**输出格式:** 插入后的数组。
|
||
|
||
**数据范围:** 原数组长度为 1~99,插入位置合法。
|
||
|
||
**输入样例:**
|
||
```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`。
|
||
|
||
**数据范围:** 数组长度为 1~100。
|
||
|
||
**输入样例:**
|
||
```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` 升序排列,然后只保留每个数的第一次出现,输出去重后的结果。
|
||
|
||
**输入格式:** 第一行输入数组长度;第二行输入数组中的所有整数。
|
||
|
||
**输出格式:** 第一行输出去重后的元素个数;第二行按升序输出去重结果。
|
||
|
||
**数据范围:** 数组长度为 1~1000,元素绝对值不超过一万。
|
||
|
||
**输入样例:**
|
||
```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` 后求中位数。元素个数为奇数时取中间值;为偶数时取中间两个数的平均值,保留一位小数。
|
||
|
||
**输入格式:** 第一行输入数组长度;第二行输入数组元素。
|
||
|
||
**输出格式:** 中位数,保留一位小数。
|
||
|
||
**数据范围:** 数组长度为 1~200,元素绝对值不超过一万。
|
||
|
||
**输入样例:**
|
||
```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:最小差值数对【略难】
|
||
|
||
**题目描述:** 输入互不相同的整数,找出差的绝对值最小的两个数,按从小到大输出;若多对差值相同,输出较小数字更小的一对。
|
||
|
||
**输入格式:** 第一行输入数组长度;第二行输入所有互不相同的整数。
|
||
|
||
**输出格式:** 满足条件的两个数和它们的差值。
|
||
|
||
**数据范围:** 数组长度为 2~200,元素绝对值不超过一万。
|
||
|
||
**输入样例:**
|
||
```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;
|
||
}
|
||
```
|
||
|
||
**易错提醒:** 排序后只需比较相邻元素;差值相同时不更新即可保留较小的一对。
|