# 第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 #include 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 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 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 #include 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 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 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 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 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 #include 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 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 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 #include 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 #include #include 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 #include 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; } ``` **易错提醒:** 排序后只需比较相邻元素;差值相同时不更新即可保留较小的一对。