为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1356
题目描述
现有两门选修课,每门选修课都有一部分学生选修,每个学生都有选修课的成绩,需要你找出同时选修了两门选修课的学生,先按照班级进行划分,班级编号小的先输出,每个班级按照两门选修课成绩和的降序排序,成绩相同时按照学生的学号升序排序。
输入描述
第一行为第一门选修课学生的成绩,
第二行为第二门选修课学生的成绩,
每行数据中学生之间以英文分号分隔,每个学生的学号和成绩以英文逗号分隔,
学生学号的格式为 8 8 8位数字
2位院系编号+入学年份后2位+院系内部1位专业编号+所在班级3位学号
学生成绩的取值范围为[ 0 , 100 0,100 0,100]之间的整数,
两门选修课选修学生数的取值范围为[ 1 − 2000 1-2000 1−2000]之间的整数。
输出描述
同时选修了两门选修课的学生的学号,如果没有同时选修两门选修课的学生输出 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 01202021、01202008、01203088,这三个学生两门选修课的成绩和分别为 150 、 150 、 185 150、150、185 150、150、185,
01202021 、 01202008 01202021、01202008 01202021、01202008届于 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 });
}
}