#P3060. 选修课(100分)
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 80
            Accepted: 46
            Difficulty: 6
            
          
          
          
                       所属公司 : 
                              华为od
                                
            
                      
          
 
- 
                        算法标签>排序算法          
 
选修课(100分)
题解
该程序的主要目的是分析学生选修课程的情况,找出同时选修两门课程的学生,并按班级和成绩排序输出。输入为两条字符串,分别表示选修课程一和课程二的学生学号及成绩。
思路丰富
- 输入解析:使用 
split函数将输入字符串分割成学生学号和成绩,并存入映射中,便于后续查找。 - 交集查找:通过遍历课程一的学生,检查他们是否在课程二的映射中存在,从而找出同时选修两门课程的学生。
 - 班级分组:根据学号的前五位进行分组,构建班级与学生之间的映射关系。
 - 排序机制:班级先按字典序排列,再根据学生的总成绩和学号进行排序,以确保输出结果的规范性和可读性。
 - 结果输出:处理完所有逻辑后,输出每个班级的学生学号,格式要求严谨,确保符合题目要求。
 
代码分析
- 
输入解析:
- 使用 
split函数将输入字符串按分号;分割,得到每个学生的学号和成绩。随后,再将每个学生的信息按逗号,分割,将学号和成绩存入映射courseMap中。 
 - 使用 
 - 
数据存储:
- 使用两个 
map<string, int>分别存储课程一和课程二的学生成绩,键为学生学号,值为成绩。 - 使用 
map<string, vector<string>>存储同时选修两门课程的学生,键为班级编号,值为该班级的学生学号列表。 
 - 使用两个 
 - 
交集查找:
- 遍历课程一的学生信息,检查每个学生是否在课程二中存在,找出同时选修两门课程的学生,并将他们按照班级分组。
 
 - 
排序:
- 首先按班级编号升序排列班级,然后对每个班级中的学生按总成绩降序、学号升序进行排序。
 
 - 
结果输出:
- 按要求格式输出每个班级的编号和对应的学生学号,若没有学生同时选修两门课程,则输出“NULL”。
 
 
cpp
#include <bits/stdc++.h>
using namespace std;
// 根据指定分隔符分割字符串
vector<string> split(string &str, char delimiter) {
    stringstream ss(str);
    string token;
    vector<string> results;
    while (getline(ss, token, delimiter)) {
        results.emplace_back(token);
    }
    return results;
}
// 解析输入字符串,构建学生成绩映射
void parse(string &str, map<string, int> &courseMap) {
    vector<string> studentEntries = split(str, ';');  // 以分号分割每个学生的信息
    for (string entry : studentEntries) {
        vector<string> info = split(entry, ',');  // 以逗号分割学号和成绩
        string studentId = info[0];
        int score = stoi(info[1]);  // 将成绩从字符串转为整数
        courseMap[studentId] = score;  // 将学号和成绩存入映射中
    }
}
int main() {
    string input1, input2;
    cin >> input1 >> input2;  // 读取两条输入字符串
    // 存储选修课程一的学生成绩
    map<string, int> courseOne;
    parse(input1, courseOne);
    // 存储选修课程二的学生成绩
    map<string, int> courseTwo;
    parse(input2, courseTwo);
    // 存储同时选修两门课程的学生学号,按班级分组
    map<string, vector<string>> classMap;
    // 找出同时选修了两门课程的学生
    for (const auto &entry : courseOne) {
        string studentId = entry.first;  // 获取学生学号
        // 检查该学生是否也选修了课程二
        if (courseTwo.find(studentId) != courseTwo.end()) {
            string classId = studentId.substr(0, 5); // 获取班级编号(学号前五位)
            classMap[classId].emplace_back(studentId);  // 将学号添加到相应班级的列表中
        }
    }
    // 如果没有学生同时选修两门课程,输出NULL
    if (classMap.empty()) {
        cout << "NULL" << endl;
        return 0;
    }
    vector<string> classIds;
    for (const auto &entry : classMap) {
        classIds.emplace_back(entry.first);  // 存储所有班级编号
    }
    // 按照班级编号升序排列
    sort(classIds.begin(), classIds.end());
    // 遍历每个班级
    for (const auto &classId : classIds) {
        // 按照学生成绩和学号排序
        sort(classMap[classId].begin(), classMap[classId].end(), [&courseOne, &courseTwo](string a, string b) {
            int scoreA = courseOne[a] + courseTwo[a];  // 计算学生A的总成绩
            int scoreB = courseOne[b] + courseTwo[b];  // 计算学生B的总成绩
            if (scoreA != scoreB) {
                // 按照总成绩降序排列
                return scoreA > scoreB;
            } else {
                // 成绩相同则按学号升序排列
                return a < b;
            }
        });
        // 输出班级编号
        cout << classId << endl;
        // 输出该班级中同时选修两门课程的学生学号
        for (int i = 0; i < classMap[classId].size(); i++) {
            cout << classMap[classId][i];
            if (i < classMap[classId].size() - 1) {
                cout << ";";  // 追加分隔符
            }
        }
        cout << endl;  // 换行
    }
    return 0;
}
java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // key为学生学号, value为该学生选修课程一的成绩
        HashMap<String, Integer> courseOneScores = new HashMap<>();
        parse(scanner.nextLine(), courseOneScores);
        // key为学生学号, value为该学生选修课程二的成绩
        HashMap<String, Integer> courseTwoScores = new HashMap<>();
        parse(scanner.nextLine(), courseTwoScores);
        // key为班级编号, value为该班级同时选修两门课程的学生学号集合
        HashMap<String, ArrayList<String>> classStudentMap = new HashMap<>();
        // 找出同时选修了两门课程的学生学号
        for (String studentId : courseOneScores.keySet()) {
            if (courseTwoScores.containsKey(studentId)) {
                // 班级编号(学号前五位)
                String classId = studentId.substring(0, 5);
                // 该班级同时选修两门课程的学生学号集合
                classStudentMap.putIfAbsent(classId, new ArrayList<>());
                classStudentMap.get(classId).add(studentId);
            }
        }
        // 如果没有同时选修两门选修课的学生输出NULL
        if (classStudentMap.isEmpty()) {
            System.out.println("NULL");
            return;
        }
        // 先按照班级划分,班级编号小的先输出(班级编号字典序升序)
        classStudentMap.keySet().stream().sorted(String::compareTo).forEach(classId -> {
            classStudentMap.get(classId).sort((a, b) -> {
                int totalScoreA = courseOneScores.get(a) + courseTwoScores.get(a);
                int totalScoreB = courseOneScores.get(b) + courseTwoScores.get(b);
                if (totalScoreA != totalScoreB) {
                    // 按照两门选修课成绩和的降序
                    return totalScoreB - totalScoreA;
                } else {
                    // 成绩和相同时按照学号升序
                    return a.compareTo(b);
                }
            });
            // 先打印班级编号
            System.out.println(classId);
            // 再打印该班级中同时选修两门课程的学生学号
            System.out.println(String.join(";", classStudentMap.get(classId)));
        });
    }
    public static void parse(String input, HashMap<String, Integer> courseMap) {
        String[] studentEntries = input.split(";");
        for (String studentEntry : studentEntries) {
            String[] info = studentEntry.split(",");
            String studentId = info[0];
            int score = Integer.parseInt(info[1]);
            courseMap.put(studentId, score);
        }
    }
}
python
def parse(input_str, course_map):
    student_entries = input_str.split(";")
    for entry in student_entries:
        student_id, score = entry.split(",")
        course_map[student_id] = int(score)
if __name__ == '__main__':
    # key为学生学号, value为该学生选修课程一的成绩
    courseOneScores = {}
    parse(input(), courseOneScores)
    # key为学生学号, value为该学生选修课程二的成绩
    courseTwoScores = {}
    parse(input(), courseTwoScores)
    # key为班级编号, value为该班级同时选修两门课程的学生学号集合
    classStudentMap = {}
    # 找出同时选修了两门课程的学生学号
    for studentId in courseOneScores:
        if studentId in courseTwoScores:
            # 班级编号(学号前五位)
            classId = studentId[:5]
            # 该班级同时选修两门课程的学生学号集合
            classStudentMap.setdefault(classId, [])
            classStudentMap[classId].append(studentId)
    # 如果没有同时选修两门选修课的学生输出NULL
    if len(classStudentMap) == 0:
        print("NULL")
    else:
        # 先按照班级划分,班级编号小的先输出(班级编号字典序升序)
        for classId in sorted(classStudentMap.keys()):
            # 按照两门选修课成绩和的降序, 成绩和相同时按照学号升序
            classStudentMap[classId].sort(key=lambda studentId: (-(courseOneScores[studentId] + courseTwoScores[studentId]), studentId))
            # 先打印班级编号
            print(classId)
            # 再打印该班级中同时选修两门课程的学生学号
            print(";".join(classStudentMap[classId]))
        题目内容
现有两门选修课,每门选修课都有一部分学生选修,每个学生都有选修课的成绩,需要你找出同时选修了两门选修课的学生,先按照班级进行划分,班级编号小的先输出,每个班级按照两门选修课成绩和的降序排序,成绩相同时按照学生的学号升序排序。
输入描述
第一行为第一门选修课学生的成绩,
第二行为第二门选修课学生的成绩,
每行数据中学生之间以英文分号分隔,每个学生的学号和成绩以英文逗号分隔,
学生学号的格式为8位数字
2位院系编号+入学年份后2位+院系内部1位专业编号+所在班级3位学号
学生成绩的取值范围为[0,100]之间的整数,
两门选修课选修学生数的取值范围为[1-2000]之间的整数。
输出描述
同时选修了两门选修课的学生的学号,如果没有同时选修两门选修课的学生输出NULL,
否则,先按照班级划分,班级编号小的先输出,每个班级先输出班级编号(学号前五位),
然后另起一行输出这个班级同时选修两门选修课的学生学号,学号按照要求排序(按照两门选修课成绩和的降序,成绩和相同时按照学号升序学生之间以英文分号分隔。
样例1
输入
01202021,75;01201033,95;01202008,80;01203006,90;01203088,100
01202008,70;01203088,85;01202111,80;01202021,75;01201100,88
输出
01202
01202008;01202021
01203
01203088
说明
同时选修了两选修课的学生01202021、01202008、01203088,这三个学生两门选修课的成绩和分别为150、150、185,
01202021、01202008属于01202班的学生,按照成绩和降序,成绩相同时按学号升序输出的结果为01202008;01202021,
01203088属于01203班的学生,按照成绩和降序,成绩相同时按学号升序输出的结果为01203088,
01202的班级编号小于01203的班级编号,需要先输出。
样例2
输入
01201022,75;01202033,95;01202018,80;01203006,90;01202066,100
01202008,70;01203102,85;01202111,80;01201021,75;01201100,88
输出
NULL
说明
没有同时选修了两门选修课的学生,输出NULL.