华为OD笔试之【排序】双机位A-比赛【Py/Java/C++/C/JS/Go六种语言OD独家双机位A卷真题】【欧弟算法】全网注释最详细分类最全的华子OD真题题解
可上 欧弟OJ系统 练习华子OD、大厂真题
绿色聊天软件戳od1441了解算法冲刺训练(备注【CSDN】否则不通过)
文章目录
相关推荐阅读
- 【华为OD机考】2025C+2025B+2024E+D卷真题【完全原创题解 | 详细考点分类 | 不断更新题目】
- 【华为OD笔试】双机位A+2025C+2025B+2024E+D卷真题机考套题汇总【真实反馈,不断更新,限时免费】
- 【华为OD笔试】2024E+D卷命题规律解读【分析500+场OD笔试考点总结】
- 【华为OD流程】性格测试选项+注意事项】
题目练习网址:【排序】双机位A-比赛
题目描述与示例
题目描述
一个有N个选手参加比赛,选手编号为1~N(3<=N<=100),有M(3<=M<=10)个评委对选手进行打分。
打分规则为每个评委对选手打分,最高分10分,最低分1分。
请计算得分最多的3位选手的编号。
如果得分相同,则得分高分值最多的选手排名靠前
(10分数量相同,则比较9分的数量,以此类推,用例中不会出现多个选手得分完全相同的情况)。
输入描述
第一行为半角逗号分割的两个正整数,第一个数字表示M(3<=M<=10)个评委,第二个数字表示N(3<=N<=100)个选手。
第2到M+1行是半角逗号分割的整数序列,表示评委为每个选手的打分,0号下标数字表示1号选手分数,1号下标数字表示2号选手分数,依次类推。
输出描述
选手前3名的编号。
注:若输入为异常,输出-1,如M、N、打分不在范围内。
示例一
输入
4,5
10,6,9,7,6
9,10,6,7,5
8,10,6,5,10
9,10,8,4,9
输出
2,1,5
说明
第一行代表有4个评委,5个选手参加比赛
矩阵代表是4*5,每个数字是选手的编号,每一行代表一个评委对选手的打分排序,
2号选手得分36分排第1,1号选手36分排第2,5号选手30分(2号10分值有3个,1号10分值只有1个,所以2号排第一)
示例二
输入
2,5
7,3,5,4,2
8,5,4,4,3
输出
-1
说明
只有2个评委,要求最少为3个评委
示例三
输入
4,2
8,5
5,6
10,4
8,9
输出
-1
说明
只有2名选手参加,要求最少为3名
示例四
输入
4,5
11,6,9,7,8
9,10,6,7,8
8,10,6,9,7
9,10,8,6,7
输出
-1
说明
第一个评委给第一个选手打分11,无效分数
解题思路
比较典型的排序题,先按照分数总和进行逆序排序,但由于在分数相同的时候要依次比较分数10到1的分数,如果直接用lambda表达式来完成会写得比较长,所以可以换成自定义函数的方式来得到函数返回值。
# nums是某个选手的分数列表,长度为M
def sort_key(nums):
# 获得每个分数出现的次数
cnt = Counter(nums)
# sum(nums)是选手的总分
# cnt[i]是选手获得i分的个数,顺序是从10到1,由于需要考虑的频率是从大到小所以考虑-cnt[i]
# 返回形如(-sum(nums), -cnt[10], -cnt[9], ..., -cnt[2], -cnt[1])这样的元组用于排序
return tuple([-sum(nums)] + [-cnt[i] for i in range(10, 0, -1)])
剩下的主要问题就是处理异常了。
异常分为两种
- 第一种是类似示例二三四所示,给出的数据不位于指定范围内
- 第二种是输入的格式有问题,譬如行数列数不够、分割符并非逗号、输入包含字母等无关字符等等
第一种错误是可以通过条件语句来排除掉的,但第二种错误因为错误种类繁杂, 我们也无法提前知道考试时候的具体测试用例中包含哪些错误,因此需要使用try-except异常处理语句来进行处理。
由于可能存在多种异常,我们不妨把整个解决问题的代码放在函数solve()中,一旦出现异常情况就可以立刻通过return来终止函数的运行。
那么整体的框架为
# 由于可能会出现多种异常,所以在一个函数中进行编写更加方便
# 可以通过return直接退出结果
def solve():
# 在try中处理计算以及判断可能出现的异常,属于第一种情况
try:
pass
# 在except中处理所有无法提前预判的异常,属于第二种情况
except:
return -1
剩下的问题就是在try的分支下面填充细节了,都是比较基础的语法内容。
代码
Python
# 欢迎来到「欧弟算法 - 华为OD全攻略」,收录华为OD题库、面试指南、八股文与学员案例!
# 地址:https://www.odalgo.com
# 华为OD机试刷题网站:https://www.algomooc.com
# 添加微信 278166530 获取华为 OD 笔试真题题库和视频
# 题目:【排序】2024E/2025B/双机位A-比赛
# 分值:100
# 作者:许老师-闭着眼睛学数理化
# 算法:模拟/排序
# 代码看不懂的地方,请直接在群上提问
from collections import Counter, defaultdict
# 用于排序的函数
# nums是某个选手的分数列表,长度为M
def sort_key(nums):
# 获得每个分数出现的次数
cnt = Counter(nums)
# sum(nums)是选手的总分,逆序排序
# cnt[i]是选手获得i分的个数,顺序是从10到1,由于需要考虑的频率是从大到小所以考虑-cnt[i]
# 返回形如(-sum(nums), -cnt[10], -cnt[9], ..., cnt[2], cnt[1])这样的元组用于排序
return tuple([-sum(nums)] + [-cnt[i] for i in range(10, 0, -1)])
# 由于可能会出现多种异常,所以在一个函数中进行编写更加方便
# 可以通过return直接退出结果
def solve():
# 由于不清楚输入可能会出现哪种异常(譬如可能出现字母等等)
# 所以整体函数放在大的try-except框架中进行编写
try:
# 得到选手数目N,评委数目M
M, N = map(int, input().split(","))
# N和M的数目没有在规定范围内,返回-1
if N < 3 or N > 100 or M < 3 or M > 10:
return -1
# 创建哈希表,key为选手编号,value为这个选手的M个分数nums
scores = defaultdict(list)
# 遍历所有评委的打分情况,构建scores哈希表的具体情况
for _ in range(M):
# 获得当前评委的所有打分情况
judges = list(map(int, input().split(",")))
# 如果这个评委打分个数不为N,则返回-1
if len(judges) != N:
return -1
# 遍历这个评委打分的所有分数
for i, score in enumerate(judges):
# 如果分数不落在1-10的范围内,则返回-1
if score > 10 or score < 1:
return -1
# 此处的+1是因为编号从1开始的,但for循环的索引是从0开始的
# 如果for循环写成 for i, score in enumerate(judges, 1)
# 则此处可以不用写上+1
scores[i+1].append(score)
global ans
# 构建完score之后,进行排序
# 且排序后取前3个结果,储存在ans中
# 如果不使用sort_key(),要写成
# ans = sorted(list(scores.keys()), key = lambda x:
# (-sum(x), -cnt[x][10], -cnt[x][9], -cnt[x][8], -cnt[x][7], -cnt[x][6],
# -cnt[x][5], -cnt[x][4], -cnt[x][3], -cnt[x][2], -cnt[x][1])[:3]
# 就会比较长
ans = sorted(list(scores.keys()), key = lambda x: sort_key(scores[x]))[:3]
# 为了保证函数返回值的统一性
# (在python中其实支持同一个函数返回不同类型的值,但其他语言不支持)
# 此处不直接返回ans,而是返回1和出现异常返回-1区分开
# ans使用全局变量的方式进行修改
return 1
except:
return -1
# 构建答案变量ans
ans = list()
# 调用solve函数,如果返回-1,说明出现了异常,输出-1
if solve() == -1:
print(-1)
# 否则输出ans的结果
else:
print(",".join(str(num) for num in ans))
Java
import java.util.*;
public class Main {
// 用于计算排序关键字的函数
// nums 是某个选手的分数列表,长度为 M
public static List<Integer> sortKey(List<Integer> nums) {
// 使用 Map 计算每个分数出现的次数
Map<Integer, Integer> cnt = new HashMap<>();
for (int num : nums) {
cnt.put(num, cnt.getOrDefault(num, 0) + 1);
}
// 构建用于排序的关键字 (-sum(nums), -cnt[10], -cnt[9], ..., cnt[2], cnt[1])
List<Integer> res = new ArrayList<>();
int sum = 0;
for (int num : nums) {
sum += num;
}
res.add(-sum); // -sum(nums)
for (int i = 10; i >= 1; i--) {
res.add(-cnt.getOrDefault(i, 0)); // -cnt[i]
}
return res;
}
// 比较两个选手的排序关键字
public static int compare(Map.Entry<Integer, List<Integer>> a, Map.Entry<Integer, List<Integer>> b) {
List<Integer> keyA = sortKey(a.getValue());
List<Integer> keyB = sortKey(b.getValue());
// 按顺序比较每个元素
for (int i = 0; i < keyA.size(); i++) {
if (!keyA.get(i).equals(keyB.get(i))) {
return keyA.get(i) - keyB.get(i); // 比较顺序
}
}
return 0;
}
// 处理逻辑
public static int solve() {
try {
// 读取选手数目 N 和评委数目 M
Scanner sc = new Scanner(System.in);
String[] input = sc.nextLine().split(",");
int M = Integer.parseInt(input[0]);
int N = Integer.parseInt(input[1]);
// N和M的数目没有在规定范围内,返回 -1
if (N < 3 || N > 100 || M < 3 || M > 10) {
return -1;
}
// 创建哈希表,key 为选手编号,value 为该选手的 M 个分数
Map<Integer, List<Integer>> scores = new HashMap<>();
// 遍历所有评委的打分情况,构建 scores 哈希表
for (int i = 0; i < M; i++) {
String[] judgeScores = sc.nextLine().split(",");
for (int j = 0; j < N; j++) {
int score = Integer.parseInt(judgeScores[j]);
if (score < 1 || score > 10) {
return -1; // 如果分数不落在 1-10 范围内,返回 -1
}
scores.computeIfAbsent(j + 1, k -> new ArrayList<>()).add(score);
}
}
// 构建结果 ans,并进行排序
List<Map.Entry<Integer, List<Integer>>> ans = new ArrayList<>(scores.entrySet());
ans.sort(Main::compare);
// 输出前 3 个结果
for (int i = 0; i < 3; i++) {
System.out.print(ans.get(i).getKey());
if (i < 2) {
System.out.print(",");
}
}
System.out.println();
return 1;
} catch (Exception e) {
return -1; // 异常处理
}
}
public static void main(String[] args) {
// 调用 solve 函数,如果返回 -1,说明出现了异常,输出 -1
if (solve() == -1) {
System.out.println(-1);
}
}
}
C++
#include <iostream>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <algorithm>
#include <map>
using namespace std;
// 用于计算排序关键字的函数
// nums 是某个选手的分数列表,长度为 M
vector<int> sortKey(const vector<int>& nums) {
// 使用 map 计算每个分数出现的次数
map<int, int> cnt;
for (int num : nums) {
cnt[num]++;
}
// 构建用于排序的关键字 (-sum(nums), -cnt[10], -cnt[9], ..., cnt[2], cnt[1])
vector<int> res;
int sum = 0;
for (int num : nums) {
sum += num;
}
res.push_back(-sum); // -sum(nums)
for (int i = 10; i >= 1; i--) {
res.push_back(-cnt[i]); // -cnt[i]
}
return res;
}
// 比较两个选手的排序关键字
bool compare(const pair<int, vector<int>>& a, const pair<int, vector<int>>& b) {
vector<int> keyA = sortKey(a.second);
vector<int> keyB = sortKey(b.second);
// 按顺序比较每个元素
for (size_t i = 0; i < keyA.size(); i++) {
if (keyA[i] != keyB[i]) {
return keyA[i] < keyB[i]; // 比较顺序
}
}
return false;
}
// 处理逻辑
int solve() {
try {
// 读取选手数目 N 和评委数目 M
string input;
getline(cin, input);
replace(input.begin(), input.end(), ',', ' ');
stringstream ss(input);
int M, N;
ss >> M >> N;
// N和M的数目没有在规定范围内,返回 -1
if (N < 3 || N > 100 || M < 3 || M > 10) {
return -1;
}
// 创建哈希表,key 为选手编号,value 为该选手的 M 个分数
unordered_map<int, vector<int>> scores;
// 遍历所有评委的打分情况,构建 scores 哈希表
for (int i = 0; i < M; i++) {
string judgeScores;
getline(cin, judgeScores);
replace(judgeScores.begin(), judgeScores.end(), ',', ' ');
stringstream ssScores(judgeScores);
vector<int> judgeMarks(N);
for (int j = 0; j < N; j++) {
ssScores >> judgeMarks[j];
if (judgeMarks[j] < 1 || judgeMarks[j] > 10) {
return -1; // 如果分数不落在 1-10 范围内,返回 -1
}
scores[j + 1].push_back(judgeMarks[j]);
}
}
// 构建结果 ans,并进行排序
vector<pair<int, vector<int>>> ans(scores.begin(), scores.end());
sort(ans.begin(), ans.end(), compare);
// 输出前 3 个结果
for (int i = 0; i < 3; i++) {
cout << ans[i].first;
if (i < 2) {
cout << ",";
}
}
cout << endl;
return 1;
} catch (...) {
return -1; // 异常处理
}
}
int main() {
// 调用 solve 函数,如果返回 -1,说明出现了异常,输出 -1
if (solve() == -1) {
cout << -1 << endl;
}
return 0;
}
C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义选手的最大数量和分数范围
#define MAX_PLAYERS 100
#define MAX_SCORES 10
// 用于存储每个选手的分数
typedef struct {
int id; // 选手编号
int scores[MAX_SCORES]; // 每个选手的分数列表
int scoreCount; // 分数数量
} Player;
// 用于计算排序关键字的函数
// nums 是某个选手的分数列表,长度为 M
void sortKey(int *nums, int scoreCount, int *res) {
// 计算每个分数出现的次数
int cnt[MAX_SCORES + 1] = {0}; // 用来存储分数出现的次数,分数范围为1-10
int sum = 0;
for (int i = 0; i < scoreCount; i++) {
cnt[nums[i]]++;
sum += nums[i];
}
// 构建用于排序的关键字 (-sum(nums), -cnt[10], -cnt[9], ..., cnt[1])
res[0] = -sum;
int idx = 1;
for (int i = 10; i >= 1; i--) {
res[idx++] = -cnt[i]; // -cnt[i]
}
}
// 比较两个选手的排序关键字
int comparePlayers(const void *a, const void *b) {
Player *playerA = (Player *)a;
Player *playerB = (Player *)b;
int keyA[MAX_SCORES + 1], keyB[MAX_SCORES + 1];
sortKey(playerA->scores, playerA->scoreCount, keyA);
sortKey(playerB->scores, playerB->scoreCount, keyB);
// 按顺序比较每个元素
for (int i = 0; i < MAX_SCORES + 1; i++) {
if (keyA[i] != keyB[i]) {
return keyA[i] - keyB[i]; // 比较顺序
}
}
return 0;
}
// 处理逻辑
int solve() {
// 读取选手数目 N 和评委数目 M
char input[1000];
fgets(input, sizeof(input), stdin);
int M, N;
sscanf(input, "%d,%d", &M, &N);
// N和M的数目没有在规定范围内,返回 -1
if (N < 3 || N > MAX_PLAYERS || M < 3 || M > MAX_SCORES) {
return -1;
}
Player players[MAX_PLAYERS]; // 用于存储所有选手的分数
for (int i = 0; i < N; i++) {
players[i].id = i + 1;
players[i].scoreCount = 0;
}
// 遍历所有评委的打分情况
for (int i = 0; i < M; i++) {
fgets(input, sizeof(input), stdin);
int scores[MAX_PLAYERS];
char *token = strtok(input, ",");
for (int j = 0; j < N && token != NULL; j++) {
int score;
sscanf(token, "%d", &score);
if (score < 1 || score > 10) {
return -1; // 如果分数不落在 1-10 范围内,返回 -1
}
players[j].scores[players[j].scoreCount++] = score;
token = strtok(NULL, ",");
}
}
// 使用 qsort 对选手进行排序
qsort(players, N, sizeof(Player), comparePlayers);
// 输出前 3 个结果
for (int i = 0; i < 3; i++) {
printf("%d", players[i].id);
if (i < 2) {
printf(",");
}
}
printf("\n");
return 1;
}
int main() {
// 调用 solve 函数,如果返回 -1,说明出现了异常,输出 -1
if (solve() == -1) {
printf("-1\n");
}
return 0;
}
Node JavaScript
const readline = require('readline');
// 创建 readline 接口
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 全局变量保存输入数据
let inputLines = [];
let currentLine = 0;
// 读取输入
rl.on('line', (line) => {
inputLines.push(line.trim());
});
// 当输入结束时,调用 solve 函数
rl.on('close', () => {
solve();
});
// 获取输入的每一行
function readLine() {
return inputLines[currentLine++];
}
// 用于计算排序关键字的函数
// nums 是某个选手的分数列表,长度为 M
function sortKey(nums) {
// 使用 Map 计算每个分数出现的次数
let cnt = new Map();
nums.forEach(num => {
cnt.set(num, (cnt.get(num) || 0) + 1);
});
// 构建用于排序的关键字 (-sum(nums), -cnt[10], -cnt[9], ..., cnt[2], cnt[1])
let res = [];
let sum = nums.reduce((a, b) => a + b, 0);
res.push(-sum); // -sum(nums)
for (let i = 10; i >= 1; i--) {
res.push(-(cnt.get(i) || 0)); // -cnt[i]
}
return res;
}
// 比较两个选手的排序关键字
function compare(a, b) {
let keyA = sortKey(a[1]);
let keyB = sortKey(b[1]);
// 按顺序比较每个元素
for (let i = 0; i < keyA.length; i++) {
if (keyA[i] !== keyB[i]) {
return keyA[i] - keyB[i]; // 比较顺序
}
}
return 0;
}
// 处理逻辑
function solve() {
try {
// 读取选手数目 N 和评委数目 M
let input = readLine().split(",");
let M = parseInt(input[0]);
let N = parseInt(input[1]);
// N 和 M 的数目没有在规定范围内,返回 -1
if (N < 3 || N > 100 || M < 3 || M > 10) {
console.log(-1);
return;
}
// 创建哈希表,key 为选手编号,value 为该选手的 M 个分数
let scores = new Map();
// 遍历所有评委的打分情况,构建 scores 哈希表
for (let i = 0; i < M; i++) {
let judgeScores = readLine().split(",");
for (let j = 0; j < N; j++) {
let score = parseInt(judgeScores[j]);
if (score < 1 || score > 10) {
console.log(-1); // 如果分数不落在 1-10 范围内,返回 -1
return;
}
if (!scores.has(j + 1)) {
scores.set(j + 1, []);
}
scores.get(j + 1).push(score);
}
}
// 构建结果 ans,并进行排序
let ans = Array.from(scores.entries());
ans.sort(compare);
// 输出前 3 个结果
let result = [];
for (let i = 0; i < 3; i++) {
result.push(ans[i][0]);
}
console.log(result.join(","));
} catch (e) {
console.log(-1); // 异常处理
}
}
Go
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
"strings"
)
// 用于计算排序关键字的函数
// nums 是某个选手的分数列表,长度为 M
func sortKey(nums []int) []int {
// 使用 Map 计算每个分数出现的次数
cnt := make(map[int]int)
for _, num := range nums {
cnt[num]++
}
// 构建用于排序的关键字 (-sum(nums), -cnt[10], -cnt[9], ..., cnt[2], cnt[1])
res := []int{}
sum := 0
for _, num := range nums {
sum += num
}
res = append(res, -sum) // -sum(nums)
for i := 10; i >= 1; i-- {
res = append(res, -cnt[i]) // -cnt[i]
}
return res
}
// 比较两个选手的排序关键字
func compare(a, b []int) bool {
keyA := sortKey(a)
keyB := sortKey(b)
// 按顺序比较每个元素
for i := 0; i < len(keyA); i++ {
if keyA[i] != keyB[i] {
return keyA[i] < keyB[i] // 比较顺序
}
}
return false
}
// 处理逻辑
func solve() int {
reader := bufio.NewReader(os.Stdin)
// 读取选手数目 N 和评委数目 M
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
inputParts := strings.Split(input, ",")
M, _ := strconv.Atoi(inputParts[0])
N, _ := strconv.Atoi(inputParts[1])
// N和M的数目没有在规定范围内,返回 -1
if N < 3 || N > 100 || M < 3 || M > 10 {
return -1
}
// 创建哈希表,key 为选手编号,value 为该选手的 M 个分数
scores := make(map[int][]int)
// 遍历所有评委的打分情况,构建 scores 哈希表
for i := 0; i < M; i++ {
judgeScores, _ := reader.ReadString('\n')
judgeScores = strings.TrimSpace(judgeScores)
scoreStrings := strings.Split(judgeScores, ",")
for j := 0; j < N; j++ {
score, _ := strconv.Atoi(scoreStrings[j])
if score < 1 || score > 10 {
return -1 // 如果分数不落在 1-10 范围内,返回 -1
}
scores[j+1] = append(scores[j+1], score)
}
}
// 构建结果 ans,并进行排序
type player struct {
id int
scores []int
}
var ans []player
for id, scoresList := range scores {
ans = append(ans, player{id, scoresList})
}
sort.Slice(ans, func(i, j int) bool {
return compare(ans[i].scores, ans[j].scores)
})
// 输出前 3 个结果
for i := 0; i < 3 && i < len(ans); i++ {
fmt.Print(ans[i].id)
if i < 2 {
fmt.Print(",")
}
}
fmt.Println()
return 1
}
func main() {
// 调用 solve 函数,如果返回 -1,说明出现了异常,输出 -1
if solve() == -1 {
fmt.Println(-1)
}
}
时空复杂度
时间复杂度:O(NlogN)。排序N个选手所需的时间复杂度。
空间复杂度:O(NM)。统计每一个选手分数信息所占的空间。
华为OD算法/大厂面试高频题算法练习冲刺训练
-
华子OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务1000+同学成功上岸!
-
课程讲师为全网200w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化
-
90+天陪伴式学习,100+直播课时,300+动画图解视频,500+LeetCode经典题,500+华为OD真题/大厂真题,还有简历修改、模拟面试、陪伴小群、资深HR对接将为你解锁
-
可上全网独家的欧弟OJ系统练习华子OD、大厂真题
-
可查看链接OD真题汇总(持续更新)
-
绿色聊天软件戳
od1441或了解更多
更多推荐


所有评论(0)