#P3042. 优秀学员统计(100分)
-
1000ms
Tried: 172
Accepted: 58
Difficulty: 2
所属公司 :
华为od
-
算法标签>排序算法
优秀学员统计(100分)
题目描述
公司某部门软件教导团正在组织新员工每日打卡学习活动,他们开展这项学习活动已经一个月了,所以想统计下这个月优秀的打卡员工。每个员工会对应一个 id,每天的打卡记录记录当天打卡员工的 id 集合,共有30天。
请你实现代码帮助统计出打卡次数 Top 5 的员工。加入打卡次数相同,将较早参与打卡的员工排在前面,如果开始参与打卡的时间还是一样,将 id 较小的员工排在前面。
注:不考虑并列的情况,按规则返回前 5 名员工的 id,如果当月打卡的员工少于 5 个,按规则排序返回所有有打卡记录的员工 id。
输入描述
- 第一行输入为新员工数量
N,表示新员工编号id为0到N-1。 - 第二行输入为30个整数,表示每天打卡的员工数量,每天至少有1名员工打卡。
- 之后30行为每天打卡的员工
id集合,id不会重复。
输出描述
按顺序输出打卡 Top 5 员工的 id,用空格隔开。
题解思路
我们要通过打卡记录统计每个员工的打卡次数,并按以下规则进行排序:
- 打卡次数最多的排前面。
- 打卡次数相同的,先打卡的员工排前面。
- 打卡次数和打卡顺序都相同的,
id小的员工排前面。
具体步骤:
-
记录打卡信息:
- 使用一个 dict/map 来记录每个员工的打卡次数。
- 使用一个 dict/map 来记录每个员工首次打卡的天数。
-
排序:
- 根据员工的打卡次数、首次打卡天数以及
id来排序。 - 结果只取前5个员工。
- 根据员工的打卡次数、首次打卡天数以及
复杂度分析
- 时间复杂度:
O(30 * N),最多需要遍历30天的打卡记录,每天最多有N名员工。 - 空间复杂度:
O(N),需要存储每个员工的打卡次数和首次打卡的日期。
代码实现
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> count(30);
for (int day = 0; day < 30; day++) {
cin >> count[day]; // 读取每天打卡的员工数量
}
map<int, int> freq; // 记录每个员工的打卡次数
map<int, int> firstDay; // 记录每个员工首次打卡的天数
// 读取每一天的打卡记录
for (int day = 0; day < 30; day++) {
for (int i = 0; i < count[day]; i++) {
int id;
cin >> id;
freq[id]++; // 统计打卡次数
if (firstDay.find(id) == firstDay.end()) {
firstDay[id] = day; // 记录首次打卡的天数
}
}
}
// 将所有员工的ID收集到一个数组中
vector<int> ids;
for (const auto [id, y] : freq) {
ids.push_back(id);
}
// 按照打卡次数、首次打卡天数和ID排序
sort(ids.begin(), ids.end(), [&](int x, int y) {
if (freq[x] != freq[y]) {
return freq[x] > freq[y]; // 按打卡次数降序
}
if (firstDay[x] != firstDay[y]) {
return firstDay[x] < firstDay[y]; // 按首次打卡天数升序
}
return x < y; // 按ID升序
});
// 输出前5个员工的ID
for (int i = 0; i < min((int) ids.size(), 5); i++) {
cout << ids[i] << " ";
}
return 0;
}
Python
def main():
# 读取新员工数量
n = int(input())
# 读取每天打卡员工数量
count = list(map(int, input().split()))
# 记录员工的打卡次数和首次打卡的天数
freq = {}
firstDay = {}
# 读取每一天的打卡记录
for day in range(30):
ids = list(map(int, input().split()))
for id in ids:
if id not in freq:
freq[id] = 0
freq[id] += 1
if id not in firstDay:
firstDay[id] = day
# 将员工ID按规则排序
sorted_ids = sorted(freq.keys(), key=lambda x: (-freq[x], firstDay[x], x))
# 输出前5个员工的ID
print(" ".join(map(str, sorted_ids[:5])))
if __name__ == "__main__":
main()
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取新员工数量
int n = sc.nextInt();
sc.nextLine(); // 读取换行符
// 读取每天打卡员工数量
int[] count = new int[30];
String[] countInput = sc.nextLine().split(" ");
for (int i = 0; i < 30; i++) {
count[i] = Integer.parseInt(countInput[i]);
}
// 记录员工的打卡次数和首次打卡的天数
Map<Integer, Integer> freq = new HashMap<>();
Map<Integer, Integer> firstDay = new HashMap<>();
// 读取每一天的打卡记录
for (int day = 0; day < 30; day++) {
String[] ids = sc.nextLine().split(" ");
for (String idStr : ids) {
int id = Integer.parseInt(idStr);
freq.put(id, freq.getOrDefault(id, 0) + 1);
if (!firstDay.containsKey(id)) {
firstDay.put(id, day);
}
}
}
// 按照打卡次数、首次打卡天数和ID排序
List<Integer> sortedIds = new ArrayList<>(freq.keySet());
sortedIds.sort((x, y) -> {
if (freq.get(x) != freq.get(y)) {
return freq.get(y) - freq.get(x); // 按打卡次数降序
}
if (firstDay.get(x) != firstDay.get(y)) {
return firstDay.get(x) - firstDay.get(y); // 按首次打卡天数升序
}
return x - y; // 按ID升序
});
// 输出前5个员工的ID
for (int i = 0; i < Math.min(sortedIds.size(), 5); i++) {
System.out.print(sortedIds.get(i) + " ");
}
}
}
题目描述
公司某部门软件教导团正在组织新员工每日打卡学习活动,他们开展这项学习活动已经一个月了,所以想统计下这个月优秀的打卡员工。每个员工会对应一个 id ,每天的打卡记录记录当天打卡员工的 id 集合,一共 30 天。
请你实现代码帮助统计出打卡次数 top5 的员工。加入打卡次数相同,将较早参与打卡的员工排在前面,如果开始参与打卡的时间还是一样,将id较小的员工排在前面。
注:不考虑并列的情况,按规则返回前 5 名员工的 id 即可,如果当月打卡的员工少于 5 个,按规则排序返回所有有打卡记录的员工 id 。
输入描述
第一行输入为新员工数量 N ,表示新员工编号 id 为 0 到 N−1,N 的范围为 [1,100]
第二行输入为 30 个整数,表示每天打卡的员工数量,每天至少有 1 名员工打卡。
之后 30 行为每天打卡的员工 id 集合,id 不会重复。
输出描述
按顺序输出打卡 top5 员工的 id ,用空格隔开。
样例1
输入
11
4 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2
0 1 7 10
0 1 6 10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
10
6 10
7 10
输出
10 0 1 7 6
说明
员工编号范围为 0 ~ 10 ,id 为 10 的员工连续打卡 30 天,排第一,id 为 0,1,6,7 的员工打卡都是两天,id 为 0,1,7 的员工在第一天就打卡,比 id 为 6 的员工早,排在前面,0,1,7 按 id升序排列,所以输出[10,0,1,7,6]$
样例2
输入
7
6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
0 1 2 3 4 5
输出
0 1 2 3 4
说明
员工编号范围为 0-6 , id 为 0,1,2,3,4,5 的员工打卡次数相同,最早开始打卡的时间也一样,所以按 id 升序返回前 5 个 id
样例3
输入
2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0 1
0 1
输出
1 0
说明
只有两名员工参与打卡,按规则排序输出两名员工的 id