【备战秋招】每日一题:2023.05-B卷-华为OD机试 - 选修课

为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1356

题目描述

现有两门选修课,每门选修课都有一部分学生选修,每个学生都有选修课的成绩,需要你找出同时选修了两门选修课的学生,先按照班级进行划分,班级编号小的先输出,每个班级按照两门选修课成绩和的降序排序,成绩相同时按照学生的学号升序排序。

输入描述

第一行为第一门选修课学生的成绩,

第二行为第二门选修课学生的成绩,

每行数据中学生之间以英文分号分隔,每个学生的学号和成绩以英文逗号分隔,

学生学号的格式为 8 8 8位数字

2位院系编号+入学年份后2位+院系内部1位专业编号+所在班级3位学号

学生成绩的取值范围为[ 0 , 100 0,100 0,100]之间的整数,

两门选修课选修学生数的取值范围为[ 1 − 2000 1-2000 12000]之间的整数。

输出描述

同时选修了两门选修课的学生的学号,如果没有同时选修两门选修课的学生输出 N U L L NULL NULL,

否则,先按照班级划分,班级编号小的先输出,每个班级先输出班级编号(学号前五位),

然后另起一行输出这个班级同时选修两门选修课的学生学号,学号按照要求排序(按照两门选修课成绩和的降序,成绩和相同时按照学号升序学生之间以英文分号分隔。

样例

输入

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 01202021、01202008、01203088 012020210120200801203088,这三个学生两门选修课的成绩和分别为 150 、 150 、 185 150、150、185 150150185

01202021 、 01202008 01202021、01202008 0120202101202008届于 01202 01202 01202班的学生,按照成绩和降序,成绩相同时按学号升序输出的结果为 01202008 ; 01202021 01202008;01202021 01202008;01202021,

01203088 01203088 01203088属于 01203 01203 01203班的学生,按照成绩和降序,成绩相同时按学号升序输出的结果为 0120308 0120308 0120308

01202 01202 01202的班级编号小于 01203 01203 01203的班级编号,需要先输出。

输入

01201022,75;01202033,95;01202018,80;01203006,90;01202066,100
01202008,70;01203102,85;01202111,80;01201021,75;01201100,88

输出

NULL

说明

没有同时选修了两门选修课的学生,输出 N U L L NULL NULL

思路:模拟+自定义排序

本题上就是一道模拟题,完全根据题意进行模拟即可。

第一步,需要自己预处理数据,将输入数据处理为每个 lesson 包括 学号 stu_num 和分数 score 两部分

第二步,需要统计出所有选择了两门选修课的学生,并计算两门课的总分。

第三步,按照第一关键字为班级号字典序从小到大排序,第二关键字为分数从大到小排序,第三关键字为学号从小到大排序
这部分就是自定义排序。

第四步,按照顺序先输出每个班级号,然后输出每个班级下的学生学号。

具体实现细节请参考代码。

时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn),即排序的复杂度,其中 n n n为输入的 lesson 数。

类似题目推荐

代码

C++

#include <bits/stdc++.h>
using namespace std;

struct Lesson {
    string stu_num;
    int score;
};

// 存储课程,包括学号和分数,数据保证了每个学生至多会在两门选修课上各选一门,即至多有两个相同的学号
vector<Lesson> lessons;

// 将字符串转换为 int 类型数字
int get_num(const string& s) {
    int num = 0;
    for (char i : s) {
        num = num * 10 + (i - '0');
    }
    return num;
}

// 将 s 按照先分号拆分为(学号,分数),再按照逗号拆分为 学号和分数两部分
void split(string s) {
    s += ";";
    vector<string> vs;
    int len = s.size();

    string tmp;
    for (int i = 0; i < len; ++i) {
        if (s[i] == ';') {
            vs.emplace_back(tmp);
            tmp = "";
        } else {
            tmp += s[i];
        }
    }

    for (auto& v: vs) {
        int p = v.find(",");
        lessons.push_back({v.substr(0, p), get_num(v.substr(p + 1))});
    }
}

void solve() {
    int c = 0;
    string s;

    while (c < 2 && getline(cin, s)) {
        if (s.empty()) continue;
        c += 1;
        split(s);
    }

    // 按第一关键字 学号字典序 从小到大排序,这样一个学生的所有选修课程都会前后相邻
    sort(lessons.begin(), lessons.end(), [](const Lesson& A, const Lesson& B) {
        return A.stu_num < B.stu_num;
    });

    // 记录了所有选择 2 门选修课的学生的学号和总分
    vector<Lesson> students;
    for (int i = 0; i < lessons.size(); ++i) {
        int j = i;
        Lesson student = {lessons[i].stu_num, 0};
        int cnt = 0;
        // 找到 lessons[i].stu_num 的所有课程,并累计其分数 student.score 和选课数量 cnt
        while (j < lessons.size() && lessons[j].stu_num == lessons[i].stu_num) {
            student.score += lessons[j].score;
            cnt += 1;
            j += 1;
        }

        // 当且仅当选课数量为 2 时,说明两门课都选择了,将这个学生的学号和两门课程总分记录下来
        if (cnt == 2) {
            students.push_back(student);
        }

        i = j - 1;
    }

    // 如果 students 为空说明没有同时选两门课的学生
    if (students.empty()) {
        cout << "NULL\n";
        return;
    }

    // 否则根据题意来,班级号为第一关键字从小到大,分数为第二关键字从大到小,学号为第三关键字从小到大排序
    sort(students.begin(), students.end(), [](const Lesson& A, const Lesson& B) {
        string cA = A.stu_num.substr(0, 5), cB = B.stu_num.substr(0, 5);
        if (cA == cB) {
            if (A.score == B.score) {
                return A.stu_num < B.stu_num;
            }
            return A.score > B.score;
        }
        return cA < cB;
    });

    // 按照题目要求输出
    for (int i = 0; i < students.size(); ++i) {
        // 先输出每个班级号
        string t = students[i].stu_num.substr(0, 5);
        cout << t << "\n";


        int j = i + 1;
        while (j < students.size() && students[j].stu_num.substr(0, 5) == t) {
            j += 1;
        }

        // 再输出这个班级下所有选了两门课的学生的学号
        for (int k = i; k < j; ++k) {
            cout << students[k].stu_num << ";\n"[k + 1 == j];
        }
        i = j - 1;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    solve();

    return 0;
}

python

class Lesson:
    def __init__(self, stu_num, score):
        self.stu_num = stu_num
        self.score = score


lessons = []
def get_lessons(lst):
    for i in lst:
        tmp = i.split(',')
        lessons.append(Lesson(tmp[0], int(tmp[1])))


def main():
    lesson1 = input().split(';')
    lesson2 = input().split(';')

    # 获取选择选修课 1 的所有学生的学号和分数
    get_lessons(lesson1)
    # 获取选择选修课 2 的所有学生的学号和分数
    get_lessons(lesson2)

    # 按第一关键字 学号字典序 从小到大排序,这样一个学生的所有选修课程都会前后相邻
    lessons.sort(key=lambda lesson: lesson.stu_num)

    # 记录了所有选择 2 门选修课的学生的学号和总分
    students = []
    i, j = 0, 0
    while i < len(lessons):
        j = i
        student = Lesson(lessons[i].stu_num, 0)
        cnt = 0
        # 找到 lessons[i].stu_num 的所有课程,并累计其分数 t.score 和选课数量 cnt
        while j < len(lessons) and lessons[j].stu_num == lessons[i].stu_num:
            student.score += lessons[j].score
            cnt += 1
            j += 1

        if cnt == 2:
            students.append(student)

        i = j

    # 如果 students 为空说明没有同时选两门课的学生
    if len(students) == 0:
        print("NULL")
        return

    # 否则根据题意来,班级号为第一关键字从小到大,分数为第二关键字从大到小,学号为第三关键字从小到大排序
    students.sort(key=lambda x: (x.stu_num[:5], -x.score, x.stu_num))

    # 按照题目要求输出
    i, j = 0, 0
    while i < len(students):
        # 先输出每个班级号
        class_num = students[i].stu_num[:5]
        print(class_num)

        j = i + 1
        while j < len(students) and students[j].stu_num[:5] == class_num:
            j += 1

        # 再输出这个班级下所有选了两门课的学生的学号
        for k in range(i, j):
            print(students[k].stu_num, end="\n" if k + 1 == j else ";")

        i = j


if __name__ == "__main__":
    main()

Java

import java.util.*;

class Lesson {
    String stu_num;
    int score;

    Lesson(String stu_num, int score) {
        this.stu_num = stu_num;
        this.score = score;
    }

}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String[] lesson1 = scanner.nextLine().split(";");
        String[] lesson2 = scanner.nextLine().split(";");

        List<Lesson> lessons = new ArrayList<>();

        // 获取选择选修课 1 的所有学生的学号和分数
        getLessons(lesson1, lessons);
        // 获取选择选修课 2 的所有学生的学号和分数
        getLessons(lesson2, lessons);

        // 按第一关键字,学号字典序从小到大排序,这样一个学生的所有选修课程都会前后相邻
        Collections.sort(lessons, new Comparator<Lesson>() {
            @Override
            public int compare(Lesson o1, Lesson o2) {
                return o1.stu_num.compareTo(o2.stu_num);
            }
        });

        List<Lesson> students = new ArrayList<>();
        int i = 0, j;

        while (i < lessons.size()) {
            j = i;
            Lesson student = new Lesson(lessons.get(i).stu_num, 0);
            int cnt = 0;

            // 找到 lessons[i].stu_num 的所有课程,并累计其分数 t.score 和选课数量 cnt
            while (j < lessons.size() && lessons.get(j).stu_num.equals(lessons.get(i).stu_num)) {
                student.score += lessons.get(j).score;
                cnt++;
                j++;
            }

            if (cnt == 2) {
                students.add(student);
            }

            i = j;
        }

        // 如果 students 为空说明没有同时选两门课的学生
        if (students.size() == 0) {
            System.out.println("NULL");
            return;
        }

        // 否则根据题意来,班级号为第一关键字从小到大,分数为第二关键字从大到小,学号为第三关键字从小到大排序
        Collections.sort(students, new Comparator<Lesson>() {
            @Override
            public int compare(Lesson o1, Lesson o2) {
                if (o1.stu_num.substring(0, 5).equals(o2.stu_num.substring(0, 5))) {
                    if (o1.score == o2.score) {
                        return o1.stu_num.compareTo(o2.stu_num);
                    }
                    return Integer.compare(o2.score, o1.score);
                }
                return o1.stu_num.substring(0, 5).compareTo(o2.stu_num.substring(0, 5));
            }
        });

        // 按照题目要求输出
        i = 0;

        while (i < students.size()) {
            // 先输出每个班级号
            String class_num = students.get(i).stu_num.substring(0, 5);
            System.out.println(class_num);

            j = i + 1;

            while (j < students.size() && students.get(j).stu_num.substring(0, 5).equals(class_num)) {
                j++;
            }

            // 再输出这个班级下所有选了两门课的学生的学号
            for (int k = i; k < j; k++) {
                System.out.print(students.get(k).stu_num);
                if (k + 1 == j) {
                    System.out.println();
                } else {
                    System.out.print(";");
                }
            }

            i = j;
        }
    }

    public static void getLessons(String[] lst, List<Lesson> lessons) {
        for (String s : lst) {
            String[] tmp = s.split(",");
            lessons.add(new Lesson(tmp[0], Integer.parseInt(tmp[1])));
        }
    }
}

Go


Js

process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
    input += data;
    return;
});

process.stdin.on('end', () => {
    const lines = input.trim().split('\n');
    const lesson1 = lines[0].trim().split(';');
    const lesson2 = lines[1].trim().split(';');
    let lessons = [];
    getLessons(lesson1, lessons);
    getLessons(lesson2, lessons);

    // 按学号字典序从小到大排序,这样一个学生的所有选修课程都会前后相邻
    lessons.sort((a, b) => a.stuNum.localeCompare(b.stuNum));

    let students = [];
    let i = 0;
    while (i < lessons.length) {
        let j = i;
        let student = { stuNum: lessons[i].stuNum, score: 0 };
        let cnt = 0;
        // 找到 lessons[i].stuNum 的所有课程,并累计其分数 t.score 和选课数量 cnt
        while (j < lessons.length && lessons[j].stuNum === lessons[i].stuNum) {
            student.score += lessons[j].score;
            cnt++;
            j++;
        }
        // 如果一个学生同时选了两门课,将其加入到 students 中
        if (cnt === 2) {
            students.push(student);
        }
        i = j;
    }

    // 如果 students 为空说明没有同时选两门课的学生
    if (students.length === 0) {
        console.log("NULL");
        return;
    }

    // 按照题目要求排序
    students.sort((a, b) => {
        if (a.stuNum.substring(0, 5) === b.stuNum.substring(0, 5)) {
            if (a.score === b.score) {
                return a.stuNum.localeCompare(b.stuNum);
            }
            return b.score - a.score;
        }
        return a.stuNum.localeCompare(b.stuNum);
    });

    // 输出结果
    i = 0;
    while (i < students.length) {
        let classNum = students[i].stuNum.substring(0, 5);
        console.log(classNum);
        let j = i + 1;
        while (j < students.length && students[j].stuNum.substring(0, 5) === classNum) {
            j++;
        }
        for (let k = i; k < j; k++) {
            process.stdout.write(students[k].stuNum);
            if (k + 1 === j) {
                console.log();
            } else {
                process.stdout.write(';');
            }
        }
        i = j;
    }
});

// 将字符串数组转换为 Lesson 结构体数组
function getLessons(lst, lessons) {
    for (let i = 0; i < lst.length; i++) {
        let tmp = lst[i].split(',');
        let score = parseInt(tmp[1]);
        lessons.push({ stuNum: tmp[0], score: score });
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
华为OD机试 数大雁是华为公司招聘流程中的其中一项测试环节,用于考察应聘者在编程方面的能力。该笔试主要考察的是计算机基础知识和编程实战能力。 华为OD机试题目通常由一系列算法编程题组成,其中涵盖了数据结构和算法、字符串操作、动态规划、图论、递归、搜索等各个方面的知识点。考生需要根据题目要求,自行设计算法并编写相应的代码,来解决问题。 这一环节的目的是为了考察应聘者在编程方面的能力,包括对算法的理解和实现能力、代码质量和效率等方面的评估。编程能力在今天的软件工程领域中十分重要,能够有效地解决实际问题,提高代码的可读性和可维护性,是评估一个程序员水平的重要指标。 参加华为OD机试数字大雁,需要具备扎实的计算机基础知识,熟练掌握编程语言和常用的数据结构和算法,并具备理解和分析问题的能力。在备战该笔试的过程中,应聘者应该注重对算法的学习和理解,并进行大量的编程实践,深入理解各类算法的原理与应用场景。在解答算法题时,可以运用递归、分治、贪心、动态规划等常用思想,将问题拆解为更小的子问题,从而更好地解决复杂的算法问题。 华为OD机试数字大雁是一个对程序员编程能力的一种考察方式,参加者需要通过编写代码解决题目,展示自己的编程实力。准备过程中,应聘者应该注意提高自己的算法能力,并进行足够的练习,积累编程经验,从而顺利通过华为OD机试数字大雁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值