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

16 KiB
Raw Permalink Blame History

第6课编程题教师版

本课知识点覆盖表

核心知识点 GESP等级 对应课堂题
数组定义、初始化、合法下标、遍历与平均值 三级工具 课堂1
顺序查找、首次与末次位置、哨兵值 三级工具 课堂2
分类统计、打擂台求最值及下标 三级工具 课堂3
原地逆序、直接调用 sort 完成升序 三级工具 课堂4

一、课堂练习4题

课堂1成绩分析报告【适中】

题目描述: 输入若干成绩,输出总分、平均分、最高分和最低分。平均分保留两位小数。

输入格式: 第一行输入学生人数;第二行输入每名学生的成绩。

输出格式: 依次输出总分、平均分、最高分和最低分。

数据范围: 学生人数为 1100成绩为 0100。

输入样例:

5
80 95 70 85 90

输出样例:

420 84.00 95 70

解题思路: 用数组保存数据并完整遍历;总分从 0 开始,最值用第一个元素初始化。

参考代码:

#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元素绝对值不超过一万。

输入样例:

7 5
2 5 3 5 5 8 1

输出样例:

2 5 3

解题思路: 第一次位置初始化为 -1遍历时首次命中记录第一次位置每次命中都更新最后位置并计数。

参考代码:

#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。

输入样例:

4
60 70 80 50
80 85 90 70

输出样例:

1 20

解题思路: 两个数组保存两次成绩;用第一名学生初始化擂主,再按进步值、本次成绩、编号依次比较。

参考代码:

#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元素绝对值不超过一万。

输入样例:

5
4 1 3 5 2

输出样例:

2 5 3 1 4
1 2 3 4 5

解题思路: 逆序只交换一半;需要排序时直接调用 sort,本课不学习手写排序算法。

参考代码:

#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元素绝对值不超过一万。

输入样例:

4
3 6 2 9

输出样例:

20

解题思路: 遍历数组并累加。

参考代码:

#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。

输入样例:

6
-2 0 5 7 0 -1

输出样例:

2 2 2

解题思路: 遍历时使用三分支更新三个计数器。

参考代码:

#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。

输入样例:

4
10 20 30 40

输出样例:

40 30 20 10

解题思路: 交换首尾对应元素,只需循环数组长度的一半。

参考代码:

#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。

输入样例:

5
1 2 3 4 5

输出样例:

2 3 4 5 1

解题思路: 暂存第一个元素,依次用后一个元素覆盖前一个位置,最后放回暂存值。

参考代码:

#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元素绝对值不超过一万。

输入样例:

6
5 8 8 3 5 7

输出样例:

7

解题思路: 先调用 sort 升序,再从倒数第二个位置向前寻找第一个小于最大值的元素。

参考代码:

#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插入位置合法。

输入样例:

4
10 20 30 40
3 25

输出样例:

10 20 25 30 40

解题思路: 从数组末尾向插入位置倒序移动,避免覆盖尚未搬走的元素。

参考代码:

#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。

输入样例:

7 3
1 3 2 3 3 5 4

输出样例:

1 2 5 4

解题思路: 用写入位置记录保留下来的元素个数,遇到非目标值就写到数组前部。

参考代码:

#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元素绝对值不超过一万。

输入样例:

7
3 1 3 2 1 5 2

输出样例:

4
1 2 3 5

解题思路: 排序后相同元素会相邻;第一个元素直接保留,后续元素与上一个元素不同才写入数组前部。

参考代码:

#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元素绝对值不超过一万。

输入样例:

6
7 1 9 3 5 11

输出样例:

6.0

解题思路: 先升序排序;奇数长度访问中间下标,偶数长度计算两个中间元素平均值。

参考代码:

#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元素绝对值不超过一万。

输入样例:

5
8 1 15 5 3

输出样例:

1 3 2

解题思路: 先调用 sort 升序;最小差值一定出现在相邻元素之间,再用打擂台处理并列规则。

参考代码:

#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;
}

易错提醒: 排序后只需比较相邻元素;差值相同时不更新即可保留较小的一对。