top1编程
← 返回题目
题解

数据统计

1 条题解

  • 0
    @ 2026-8-6 0:54:22

    P4664 数据统计(提高)

    解题思路

    第一步,读懂题目。 有 n 名同学,每人有姓名和语文、数学、英语三科成绩。要算出平均分,评出等级(平均分大于 95 是 excellent,60~95 之间是 good,小于等于 60 是 bad),然后按平均分从高到低输出前 m 名;如果平均分相同,先输入的排在前面。

    第二步,想想生活里的例子。 老师在排"学习之星"名单,先按平均分排队,平均分一样高的同学就按报名顺序站,不能乱了次序。比如小明和小红的平均分一样都是 90 分,小明先输入,就还是小明排在前面。

    第三步,用结构体打包数据。 用结构体 Student 把姓名、三科成绩、平均分打包在一起。平均分用 (chinese+math+english)/3.0 计算,注意要除以 3.0 而不是 3,这样才是小数,不然会丢掉小数部分。

    第四步,用归并排序按平均分降序排。 题目要求用归并排序。合并时如果左边平均分 >= 右边,就把左边放进临时数组,这样平均分相等时左边的(先输入的)会先出来,天然保持输入顺序。

    第五步,输出前 m 名。 排完后取前 m 个,按等级规则判断并保留两位小数输出。注意 m 是在 n 个学生之后才输入的,要读完全部学生再读 m,这个顺序不能错。

    第六步,注意边界细节。 平均分恰好 60 分算 bad,恰好 95 分算 good,判断时注意等于号;保留两位小数用 printf 的 %.2f。n 最大接近 100000,数组开 100005 才够。另外平均分是用 double 存的小数,和整数 60、95 比较时直接用大于、大于等于就行,不用担心精度问题。

    参考代码

    // P4664 数据统计:结构体存姓名和三科成绩,归并排序按平均分降序,并列保持输入顺序
    #include <iostream>
    #include <cstdio>
    
    struct Student {
        char name[50];
        int chinese, math, english;
        double average;
    };
    
    Student students[100005], temp[100005];
    
    void mergeSort(int l, int r) {
        if (l >= r) return;
        int mid = (l + r) / 2;
        mergeSort(l, mid);
        mergeSort(mid + 1, r);
        int i = l, j = mid + 1, k = l;
        while (i <= mid && j <= r) {
            if (students[i].average >= students[j].average) temp[k++] = students[i++];   // >= 保证并列时先取左边,保持输入顺序
            else temp[k++] = students[j++];
        }
        while (i <= mid) temp[k++] = students[i++];
        while (j <= r) temp[k++] = students[j++];
        for (int p = l; p <= r; p++) students[p] = temp[p];
    }
    
    int main() {
        int n, m;
        std::scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            std::scanf("%s%d%d%d", students[i].name, &students[i].chinese, &students[i].math, &students[i].english);
            students[i].average = (students[i].chinese + students[i].math + students[i].english) / 3.0;   // 平均分
        }
        std::scanf("%d", &m);
        mergeSort(0, n - 1);
        for (int i = 0; i < m; i++) {
            const char* level = "bad";
            if (students[i].average > 95) level = "excellent";
            else if (students[i].average >= 60) level = "good";
            std::printf("%s %s %.2f\n", students[i].name, level, students[i].average);
        }
        return 0;
    }
    

    复杂度分析

    归并排序时间复杂度 O(n log n),n 最大接近 100000,约 170 万次比较,很快。归并排序是稳定排序,正好满足"平均分相同按输入顺序"的要求。空间上需要一个结构体数组 students 和一个临时数组 temp,约 12MB,在限制内。输出 m 个同学是 O(m)。这道题把"结构体 + 稳定排序 + 保留小数"三个知识点串在一起,很值得练习。

    • 1