「PAT乙级真题解析」Basic Level 1085 PAT单位排行 (问题分析+完整步骤+伪代码描述+提交通过代码)

题设给定了一组学生的信息(准考证号, 得分, 学校), 要求以学校为单位, 统计每个学校的考生人数以及加权总分。 然后将统计结果按照分数排名, 按照排名由高到低输出各个学校的排名、名称、加权总分和考生人数。 所以过程主要分为统计(数据存储)和排序输出两个部分

#算法#数据结构#pat考试#需求分析#c语言

Table of Contents

乙级的题目训练主要用来熟悉编程语言的语法和形成良好的编码习惯和编码规范。从小白开始逐步掌握用编程解决问题。

PAT (Basic Level) Practice 1085 PAT单位排行

问题分析

  • 题设给定了一组学生的信息(准考证号, 得分, 学校), 要求以学校为单位, 统计每个学校的考生人数以及加权总分。
  • 然后将统计结果按照分数排名, 按照排名由高到低输出各个学校的排名、名称、加权总分和考生人数。
  • 所以过程主要分为统计(数据存储)和排序输出两个部分

完整描述步骤

  1. 获取输入: 考生人数, 各个考生信息
  2. 将考生信息按照学校排序
  3. 初始化记录器:
    • 学校信息 = {}
  4. 对于每一个考生:
    • 学校信息[考生.所属学校].总分 = 考生.成绩 * 权重
  5. 将学校信息按照总分进行排序
  6. 输出各个学校的排名和考生人数

伪代码描述

  1. get input: student_amount, students
  2. init recorder:
    • all_schools = {}
    • current_school_index = -1;
  3. sort students by school_name
  4. for student_index in range(0, len(students)):
    • if index == 0 or students[student_index].school_name != students[student_index-1].school_name:
      • current_school_index++;
    • current_student = students[student_index];
    • all_schools[current_school_index].score = current_student.score * level_weight[current_student.level];
    • all_schools[current_school_index].student_amount++;
  5. sort all_schools by score and student_amount;
  6. all_schools[0].rank = 1;
  7. for school_index in range(1, len(all_schools)):
    • if all_schools[school_index].score == all_schools[school_index-1]:

      • all_schools[school_index].rank = all_schools[school_index-1].rank;
    • else:

      • all_schools[school_index].rank = school_index + 1;
    • current_school = all_schools[school_index]

    • print("{current_school.rank} {current_school.school_name} {current_school.score} {current_school.student_amount}");

注意事项

  1. 测试点5 超时问题:
    • 如果每次读取学生信息后, 循环访问学校信息数组, 用strcmp对比学生学校名称和当前学校的名称是否相同来确认是否将该学生的得分加入学校总分中, 会导致超时;
    • 如果想用bsearch需要先让学校信息按照名称字段有序, 每多一个学校就qsort之后bsearch也会超时
    • 如果学校名称是由6位英文字母组成, 考虑用字母的序号作为索引, 用6维数组作为存储学校名称对应的数据位置
      • 比如: "au" 对于的字母序号为 (0, 20, 0, 0, 0, 0)
      • 如果array[0][20][0][0][0][0]的值不为0, 则值对应了在学校信息数组中该学校的位置
      • 如果值为0, 说明这个学校名称是第一次出现, 应该在学校信息数组的下一个空位置插入这个学校信息
      • 该方法会出现"内存超限"的错误
    • 如果将"au"通过 0 * 10^5 + 20 * 10^4 + 0 * 10^3 + ... + 0 * 10^0的方式转换为整数
      • 会出现不同学校名转换成相同的大整数的情况, 不可行
    • 如果是将"au"对应到"00 20 00 00 00 00", 得到大整数2000000000
      • 最大值会是252525252525, 一是超过整型的表示范围可以考虑用long long 类型, 但是需要的数组空间过大, 会内存超限
  2. 超时解决方案:
    • 先将学生信息全部录入
    • 然后将学生信息按照学校名称排序
    • 然后将学生的得分录入到学校信息中

完整提交代码

/*
# 问题分析
题设给定了一组学生的信息(准考证号, 得分, 学校), 要求以学校为单位, 统计每个学校的考生人数以及加权总分。
然后将统计结果按照分数排名, 按照排名由高到低输出各个学校的排名、名称、加权总分和考生人数。
所以过程主要分为统计(数据存储)和排序输出两个部分
 
# 完整描述步骤
1. 获取输入: 考生人数, 各个考生信息
2. 将考生信息按照学校排序
3. 初始化记录器:
    - 学校信息 = {}
4. 对于每一个考生:
    - 学校信息[考生.所属学校].总分 = 考生.成绩 * 权重
5. 将学校信息按照总分进行排序
6. 输出各个学校的排名和考生人数
 
# 伪代码描述
1. get input: student_amount, students
2. init recorder:
    - all_schools = {}
    - current_school_index = -1;
3. sort students by school_name
4. for student_index in range(0, len(students)):
    - if index == 0 or students[student_index].school_name != students[student_index-1].school_name:
        - current_school_index++;
    - current_student = students[student_index];
    - all_schools[current_school_index].score = current_student.score * level_weight[current_student.level];
    - all_schools[current_school_index].student_amount++;
5. sort all_schools by score and student_amount;
6. all_schools[0].rank = 1;
7. for school_index in range(1, len(all_schools)):
    - if all_schools[school_index].score == all_schools[school_index-1]:
        - all_schools[school_index].rank = all_schools[school_index-1].rank;
    - else:
        - all_schools[school_index].rank = school_index + 1;
 
    - current_school = all_schools[school_index]
    - print("{current_school.rank} {current_school.school_name} {current_school.score} {current_school.student_amount}");
 
# 注意事项
1. 测试点5 超时问题:
    - 如果每次读取学生信息后, 循环访问学校信息数组, 用strcmp对比学生学校名称和当前学校的名称是否相同来确认是否将该学生的得分加入学校总分中, 会导致超时;
    - 如果想用bsearch需要先让学校信息按照名称字段有序, 每多一个学校就qsort之后bsearch也会超时
    - 如果学校名称是由6位英文字母组成, 考虑用字母的序号作为索引, 用6维数组作为存储学校名称对应的数据位置
        - 比如: "au" 对于的字母序号为 (0, 20, 0, 0, 0, 0)
        - 如果array[0][20][0][0][0][0]的值不为0, 则值对应了在学校信息数组中该学校的位置
        - 如果值为0, 说明这个学校名称是第一次出现, 应该在学校信息数组的下一个空位置插入这个学校信息
        - 该方法会出现"内存超限"的错误
    - 如果将"au"通过 0 * 10^5 + 20 * 10^4 + 0 * 10^3 + ... + 0 * 10^0的方式转换为整数
        - 会出现不同学校名转换成相同的大整数的情况, 不可行
    - 如果是将"au"对应到"00 20 00 00 00 00", 得到大整数2000000000
        - 最大值会是252525252525, 一是超过整型的表示范围可以考虑用long long 类型, 但是需要的数组空间过大, 会内存超限
2. 超时解决方案:
    - 先将学生信息全部录入
    - 然后将学生信息按照学校名称排序
    - 然后将学生的得分录入到学校信息中
*/
 
 
# include<string.h>
# include<stdio.h>
# include<ctype.h>
 
typedef struct {
    int rank;
    char name[7];
    int basic_level_total_score;
    int advanced_level_total_score;
    int top_level_total_score;
    int student_amount;
    int total_score;
} school;
 
typedef struct {
    char ID[7];
    int score;
    char school_name[7];
} student;
 
int calculate_total_score(school current_school){
    return current_school.basic_level_total_score / 1.5 + current_school.advanced_level_total_score + current_school.top_level_total_score * 1.5;
}
 
int cmp(const void* a, const void* b){
    school school_a = *(school*) a;
    school school_b = *(school*) b;
    return school_a.total_score != school_b.total_score ? 
            school_a.total_score < school_b.total_score : 
            school_a.student_amount != school_b.student_amount ? school_a.student_amount > school_b.student_amount : strcmp(school_a.name, school_b.name);
}
 
int cmp_student(const void* a, const void* b){
    student A = *(student *)a;
    student B = *(student *)b;
    return strcmp(A.school_name, B.school_name);
}
 
school all_schools[100000];
 
 
int main(){
    int student_amount;
    scanf("%d", &student_amount);
 
    student students[student_amount];
 
    for (int i = 0; i < student_amount; i++){
        scanf("%s %d %s", students[i].ID, &students[i].score, students[i].school_name);
        for (int j = 0; students[i].school_name[j]; j++){
            students[i].school_name[j] = tolower(students[i].school_name[j]);
        }
    }
    
    qsort(students, student_amount, sizeof(student), cmp_student);
    
    int school_index = -1;
    for (int i = 0; i < student_amount; i++){
        if (i == 0 || strcmp(students[i].school_name, students[i-1].school_name) != 0){
            school_index++;
            strcpy(all_schools[school_index].name, students[i].school_name);
        }
 
        int score = students[i].score;
        if (students[i].ID[0] == 'B') {
            all_schools[school_index].basic_level_total_score += score;
        } else if (students[i].ID[0] == 'A') {
            all_schools[school_index].advanced_level_total_score += score;
        } else {
            all_schools[school_index].top_level_total_score += score;
        }
        all_schools[school_index].student_amount++;
    }
 
 
    int school_amount = school_index + 1;
    for (int i = 0; i < school_amount; i++){
        all_schools[i].total_score = calculate_total_score(all_schools[i]);
    }
    
    qsort(all_schools, school_amount, sizeof(school), cmp);
    
    printf("%d\n", school_amount);
    all_schools[0].rank = 1;
    printf("%d %s %d %d\n", 
           all_schools[0].rank,
           all_schools[0].name, 
           all_schools[0].total_score,
           all_schools[0].student_amount
          );
    for (int i = 1; i < school_amount; i++){
        if (all_schools[i].total_score == all_schools[i-1].total_score){
            all_schools[i].rank = all_schools[i-1].rank;
        } else {
            all_schools[i].rank = i+1;
        }
        printf("%d %s %d %d\n", 
               all_schools[i].rank,
               all_schools[i].name, 
               all_schools[i].total_score,
               all_schools[i].student_amount
              );
    }
    return 0;
}
「PAT乙级真题解析」Basic Level 1085 PAT单位排行 (问题分析+完整步骤+伪代码描述+提交通过代码) | 生活糖果