【2024年华为OD机试】(B卷,100分)- 比赛 (Java JS PythonC/C++)
一、问题描述
题目解析
题目描述
一个有 N
个选手参加比赛,选手编号为 1~N
(3 <= N <= 100
),有 M
个评委对选手进行打分(3 <= M <= 10
)。每个评委对选手打分的范围为 1
到 10
分。请计算得分最多的 3 位选手的编号。如果得分相同,则得分高分值最多的选手排名靠前(即比较 10
分的数量,如果相同则比较 9
分的数量,以此类推)。
输入描述
- 第一行为半角逗号分割的两个正整数,第一个数字表示
M
个评委,第二个数字表示N
个选手。 - 第 2 到
M+1
行是半角逗号分割的整数序列,表示评委为每个选手的打分,0
号下标数字表示1
号选手分数,1
号下标数字表示2
号选手分数,依次类推。
输出描述
输出选手前 3 名的编号。如果输入异常(如 M
、N
、打分不在范围内),则输出 -1
。
用例
输入
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 个选手。
- 2 号选手得分 36 分排第 1,1 号选手得分 36 分排第 2(2 号选手有 3 个
10
分,1 号选手只有 1 个10
分),5 号选手得分 30 分排第 3。
输入
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
,无效分数。
题目解析
问题分析
-
目标
根据评委的打分,计算每个选手的总得分,并按照得分和高分值数量排序,输出前 3 名选手的编号。 -
关键点
- 每个选手的总得分为所有评委打分的总和。
- 如果总得分相同,则比较
10
分的数量,如果相同则比较9
分的数量,以此类推。 - 需要处理输入异常情况(如
M
、N
不在范围内,或打分不在1
到10
分之间)。
-
问题转化
通过统计每个选手的总得分和高分值数量,按照规则排序,输出前 3 名。
解题思路
-
输入检查
- 检查
M
和N
是否在有效范围内(3 <= M <= 10
,3 <= N <= 100
)。 - 检查每个评委的打分是否在
1
到10
分之间。
- 检查
-
统计得分和高分值数量
- 对于每个选手,计算总得分。
- 统计每个选手的
10
分、9
分等高分值的数量。
-
排序规则
- 按照总得分从高到低排序。
- 如果总得分相同,则按照
10
分数量从高到低排序,如果10
分数量相同,则比较9
分数量,以此类推。
-
输出结果
- 输出前 3 名选手的编号。
示例分析
输入
4,5
10,6,9,7,6
9,10,6,7,5
8,10,6,5,10
9,10,8,4,9
步骤
- 检查输入:
M = 4
,N = 5
,符合范围。- 所有打分在
1
到10
分之间。
- 统计每个选手的总得分和高分值数量:
- 1 号选手:总得分
10 + 9 + 8 + 9 = 36
,10
分数量1
。 - 2 号选手:总得分
6 + 10 + 10 + 10 = 36
,10
分数量3
。 - 3 号选手:总得分
9 + 6 + 6 + 8 = 29
,10
分数量0
。 - 4 号选手:总得分
7 + 7 + 5 + 4 = 23
,10
分数量0
。 - 5 号选手:总得分
6 + 5 + 10 + 9 = 30
,10
分数量1
。
- 1 号选手:总得分
- 排序:
- 2 号选手(36 分,
10
分数量3
)。 - 1 号选手(36 分,
10
分数量1
)。 - 5 号选手(30 分,
10
分数量1
)。
- 2 号选手(36 分,
- 输出结果:
2,1,5
。
复杂度分析
-
时间复杂度
- 输入检查:
O(M * N)
。 - 统计得分和高分值数量:
O(M * N)
。 - 排序:
O(N log N)
。 - 总时间复杂度:
O(M * N + N log N)
。
- 输入检查:
-
空间复杂度
- 存储每个选手的得分和高分值数量:
O(N)
。 - 总空间复杂度:
O(N)
。
- 存储每个选手的得分和高分值数量:
总结
本题的核心是通过统计每个选手的总得分和高分值数量,按照规则排序,输出前 3 名选手的编号。关键在于:
- 检查输入合法性。
- 统计每个选手的得分和高分值数量。
- 按照总得分和高分值数量排序。
- 时间复杂度为
O(M * N + N log N)
,适合处理M
和N
较小的输入。
二、JavaScript算法源码
这段 JavaScript 代码实现了一个在 ACM 模式下的控制台输入输出获取系统。它处理了一个多行输入,获取了一些分数信息,并根据规则对选手进行排序,最终输出前三名选手的编号。
代码解析
1. 输入获取
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const lines = [];
let m, n;
rl.on("line", (line) => {
lines.push(line);
if (lines.length === 1) {
[m, n] = lines[0].split(",").map(Number); // 获取m和n的值
}
if (m && lines.length === m + 1) {
lines.shift();
const scores = lines.map((line) => line.split(",").map(Number)); // 获取每个选手的成绩
console.log(getResult(scores, m, n)); // 调用函数计算结果并输出
lines.length = 0; // 重置lines数组
}
});
readline
模块:用于处理控制台的输入输出。lines
数组:用来保存从控制台输入的所有行。m, n
变量:m
:代表成绩数据的行数,即每个选手参加的比赛数。n
:代表选手的数量。
rl.on("line", (line) => {...})
:每当输入一行时,这个回调函数会被调用。通过它,我们可以读取并处理每一行输入。- 第一行输入包含了
m
和n
(用逗号隔开)。 - 后续的
m
行输入是选手的成绩数据。 - 当数据完全输入后,调用
getResult
函数进行处理,并输出前三名选手的编号。
- 第一行输入包含了
2. getResult
函数
function getResult(scores, m, n) {
if (m < 3 || m > 10 || n < 3 || n > 100) return -1;
const players = [];
for (let j = 0; j < n; j++) {
const player = [];
for (let i = 0; i < m; i++) {
if (scores[i][j] > 10 || scores[i][j] < 1) return "-1";
player.push(scores[i][j]); // 收集选手的每一项成绩
}
player.sort((a, b) => b - a); // 对成绩进行降序排序
players.push({
idx: j,
sum: player.reduce((p, c) => p + c), // 计算成绩总和
score: player,
});
}
return players
.sort((a, b) => {
if (a.sum != b.sum) return b.sum - a.sum; // 如果总和不同,按总和排序
for (let i = 0; i < m; i++) {
if (a.score[i] === b.score[i]) continue;
return b.score[i] - a.score[i]; // 如果总和相同,按成绩逐项比较
}
return 0;
})
.slice(0, 3) // 获取前3名
.map((a) => a.idx + 1) // 将选手的编号加1并输出
.join(","); // 以逗号分隔返回结果
}
-
参数:
scores
:一个二维数组,表示每个选手的成绩。m
:每个选手的成绩条目数(比赛数)。n
:选手的数量。
-
处理过程:
- 首先,检查
m
和n
是否符合规定的范围。如果不符合,直接返回-1
。 - 对每个选手的成绩进行处理:
- 如果成绩不在合理范围(1 到 10),返回
-1
。 - 将每个选手的成绩降序排序。
- 计算每个选手的成绩总和,并保存选手的排序信息(包括总和和排序后的成绩)。
- 如果成绩不在合理范围(1 到 10),返回
- 对所有选手按总成绩进行降序排序。如果总成绩相同,则逐项比较成绩,按成绩降序排序。
- 最后,选出前三名选手,返回他们的编号(加1是因为编号从1开始)。
- 首先,检查
3. rl.on("line")
的处理流程
- 第一行输入包含两个整数
m
和n
,分别表示成绩条目数和选手数量。 - 后续的
m
行输入是每个选手的成绩信息,每行包含n
个整数,表示每个选手在m
场比赛中的成绩。 - 完成输入后,调用
getResult
函数处理数据,并输出前 3 名选手的编号。
例子
输入:
3,5
8,9,6,7,8
9,7,8,6,10
7,9,8,7,9
m = 3
:每个选手参加了 3 场比赛。n = 5
:有 5 个选手。
每个选手的成绩如下:
- 选手 1:8, 9, 6
- 选手 2:9, 7, 8
- 选手 3:7, 9, 8
- 选手 4:7, 6, 7
- 选手 5:8, 10, 9
过程:
- 将每个选手的成绩进行排序:
- 选手 1:9, 8, 6,总和为 23
- 选手 2:9, 8, 7,总和为 24
- 选手 3:9, 8, 7,总和为 24
- 选手 4:7, 7, 6,总和为 20
- 选手 5:10, 9, 8,总和为 27
- 按照总成绩降序排序后:
- 选手 5:27
- 选手 2:24
- 选手 3:24
- 选手 1:23
- 选手 4:20
- 输出前三名选手的编号:
5,2,3
输出:
5,2,3
总结
这段 JavaScript 代码完成了以下功能:
- 通过 ACM 模式获取多行输入。
- 对每个选手的成绩进行处理,排序并计算成绩总和。
- 按照总成绩(以及若总成绩相同则按具体成绩逐项比较)排序,输出前三名选手的编号。
三、Java算法源码
这段 Java 代码实现了 ACM 模式的选手成绩处理功能,通过输入选手的成绩数据,计算出前 3 名选手的编号,并根据成绩总和及个别项的成绩排序。下面是对代码的详细解析。
1. 输入获取
Scanner sc = new Scanner(System.in).useDelimiter("[,\n]");
int m = sc.nextInt();
int n = sc.nextInt();
int[][] scores = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
scores[i][j] = sc.nextInt();
}
}
-
Scanner sc = new Scanner(System.in).useDelimiter("[,\n]");
:- 创建一个
Scanner
对象,用于从标准输入中读取数据。 useDelimiter("[,\n]")
设置输入的分隔符为逗号(,
)或换行符(\n
),这样就可以读取按逗号分隔的数字或每行输入的成绩。
- 创建一个
-
int m = sc.nextInt();
和int n = sc.nextInt();
:- 读取输入的前两项,
m
表示成绩数据的行数(即比赛数),n
表示选手的数量。
- 读取输入的前两项,
-
int[][] scores = new int[m][n];
:- 创建一个
m x n
的二维数组scores
用于存储每个选手的成绩。
- 创建一个
-
for
循环:- 读取接下来的
m * n
个成绩数据,并将它们存储到scores
数组中。
- 读取接下来的
2. 主逻辑:getResult
方法
public static String getResult(int m, int n, int[][] scores) {
if (m < 3 || m > 10 || n < 3 || n > 100) return "-1";
HashMap<Integer, Integer[]> players = new HashMap<>();
-
参数说明:
m
:每个选手参加的比赛数(行数)。n
:选手的数量(列数)。scores
:一个m x n
的二维数组,存储每个选手在每场比赛中的成绩。
-
if (m < 3 || m > 10 || n < 3 || n > 100) return "-1";
:- 判断输入是否合法。如果
m
或n
不符合规定的范围(3 ≤ m ≤ 10,3 ≤ n ≤ 100),则返回-1
。
- 判断输入是否合法。如果
-
HashMap<Integer, Integer[]> players = new HashMap<>();
:- 使用
HashMap
来存储选手的成绩数据,Integer
为选手编号(0 到 n-1),Integer[]
为该选手的成绩数组。
- 使用
处理每个选手的成绩:
for (int j = 0; j < n; j++) {
Integer[] player = new Integer[m];
for (int i = 0; i < m; i++) {
if (scores[i][j] > 10 || scores[i][j] < 1) return "-1"; // 队员得分1~10合法,否则不合法
player[i] = scores[i][j];
}
Arrays.sort(player, (a, b) -> b - a); // 将每个队员的得分降序
players.put(j, player);
}
-
遍历每个选手(
j
为选手的索引):- 创建一个
Integer[] player
数组,用来存储该选手的成绩。 - 对每个选手的成绩进行检查,如果有不合法的成绩(超出 1 到 10 的范围),则返回
"-1"
。 - 将成绩存入
player
数组中,并按照降序排序。
- 创建一个
-
Arrays.sort(player, (a, b) -> b - a);
:- 对每个选手的成绩数组进行降序排序,以便后续比较时使用。
-
players.put(j, player);
:- 将选手的成绩存入
players
映射表中,选手的编号为j
。
- 将选手的成绩存入
3. 排序并输出前 3 名
StringJoiner sj = new StringJoiner(",");
players.entrySet().stream()
.sorted(
(a, b) -> {
Integer[] playerA = a.getValue();
Integer[] playerB = b.getValue();
int sumA = sum(playerA);
int sumB = sum(playerB);
if (sumA != sumB) { // 按总分降序
return sumB - sumA;
}
for (int i = 0; i < m; i++) {
if (playerA[i] == playerB[i]) continue;
return playerB[i] - playerA[i]; // 按最高分降序
}
return 0;
})
.limit(3)
.forEach(p -> sj.add(p.getKey() + 1 + ""));
-
StringJoiner sj = new StringJoiner(",");
:- 创建一个
StringJoiner
用于拼接最终结果。每个选手的编号会通过逗号连接。
- 创建一个
-
players.entrySet().stream().sorted(...)
:- 使用 Java 8 Stream API 对
players
进行排序。 - 排序规则:
- 首先根据每个选手的总成绩(
sumA
和sumB
)降序排序。如果总成绩不相同,直接按照总成绩排序。 - 如果总成绩相同,再按成绩的具体项目进行逐项比较,按每个成绩降序排列。
- 首先根据每个选手的总成绩(
- 使用 Java 8 Stream API 对
-
limit(3)
:- 只取排序后的前三名选手。
-
forEach(p -> sj.add(p.getKey() + 1 + ""));
:- 将前三名选手的编号(
p.getKey()
)添加到StringJoiner
中,+ 1
是因为选手编号从 1 开始。
- 将前三名选手的编号(
4. 计算总成绩的辅助方法
public static int sum(Integer[] arr) {
return Arrays.stream(arr).reduce(Integer::sum).orElse(0);
}
- 这个方法通过
Arrays.stream(arr).reduce(Integer::sum)
来计算arr
数组中所有成绩的和。
5. 返回最终结果
return sj.toString();
- 最终将拼接的前三名选手编号返回。
总结
这段 Java 代码通过输入选手的成绩数据,对选手进行排序,并输出前三名选手的编号。排序是根据选手的总成绩进行的,如果总成绩相同,则根据单项成绩进行逐项比较。程序结构清晰,使用了 Java 8 的流式处理(Stream API)来进行排序和限制输出。
四、Python算法源码
这段 Python 代码的功能与之前 Java 代码相同,也是根据选手的成绩排序并输出前 3 名选手的编号。下面是对这段代码的详细解析:
1. 输入获取
m, n = map(int, input().split(","))
scores = [list(map(int, input().split(","))) for _ in range(m)]
-
m, n = map(int, input().split(","))
:
通过input()
获取用户输入的第一行,使用逗号(,
)分隔后,将其转换为两个整数m
和n
。m
表示比赛的数量(行数),n
表示选手的数量(列数)。 -
scores = [list(map(int, input().split(","))) for _ in range(m)]
:
通过列表推导式读取m
行输入的成绩数据,每行由逗号分隔,使用map(int, ...)
将其转化为整数并存储在二维列表scores
中。这里scores[i][j]
代表第i
场比赛中第j
位选手的成绩。
2. 排序比较函数:cmp
def cmp(a, b):
if a["sum"] != b["sum"]:
return b["sum"] - a["sum"]
for i in range(m):
if a["score"][i] == b["score"][i]:
continue
return b["score"][i] - a["score"][i]
return 0
-
cmp
函数:
这是一个用于排序的比较函数,接收两个选手的信息a
和b
,并根据以下规则返回:- 按总分降序排序:如果两个选手的总分
sum
不相同,返回它们的总分差。 - 如果总分相同,则按逐项成绩降序排序:逐个比较选手的成绩,如果某个项目的成绩不同,按该项目成绩的差值进行排序。
- 若总分和各项成绩都相同,返回
0
,表示两个选手相同。
- 按总分降序排序:如果两个选手的总分
-
functools.cmp_to_key(cmp)
:
cmp
函数需要被cmp_to_key
包装成排序键,才能在sort()
方法中使用。
3. 算法入口:getResult
def getResult():
if m < 3 or m > 10 or n < 3 or n > 100:
return "-1"
players = []
for j in range(n):
player = []
for i in range(m):
if scores[i][j] > 10 or scores[i][j] < 1:
return "-1"
player.append(scores[i][j])
player.sort(reverse=True)
players.append({
"idx": j,
"sum": sum(player),
"score": player
})
players.sort(key=functools.cmp_to_key(cmp))
return ",".join(list(map(lambda x: str(x["idx"] + 1), players[:3])))
-
合法性检查:
if m < 3 or m > 10 or n < 3 or n > 100:
:
如果m
或n
不在规定的范围内(3 ≤ m ≤ 10,3 ≤ n ≤ 100),函数直接返回"-1"
,表示数据不合法。
-
生成选手数据:
for j in range(n):
:
遍历每个选手(j
为选手的索引),为每个选手生成一个成绩列表。for i in range(m):
:
遍历每场比赛的成绩(i
为比赛的索引),将每个选手在各场比赛的成绩保存在player
列表中。- 合法性检查:
- 如果某个成绩不在合法范围(1 到 10 之间),函数返回
"-1"
。
- 如果某个成绩不在合法范围(1 到 10 之间),函数返回
player.sort(reverse=True)
:
对每个选手的成绩进行降序排序,确保后续的比较按从高到低的顺序。players.append({...})
:
将每个选手的编号(idx
)、总成绩(sum
)和排序后的成绩(score
)存储到players
列表中。
-
排序:
players.sort(key=functools.cmp_to_key(cmp))
:
使用自定义的比较函数cmp
对所有选手进行排序。
-
返回结果:
return ",".join(list(map(lambda x: str(x["idx"] + 1), players[:3])))
:
获取排序后的前三名选手的编号(从 1 开始),并通过逗号连接成一个字符串返回。
4. 调用函数并输出
print(getResult())
- 调用
getResult()
函数,输出排序后的前三名选手编号。
例子
输入:
3,5
8,9,6,7,8
9,7,8,6,10
7,9,8,7,9
m = 3
:每个选手参加了 3 场比赛。n = 5
:有 5 个选手。
每个选手的成绩如下:
- 选手 1:8, 9, 6
- 选手 2:9, 7, 8
- 选手 3:7, 9, 8
- 选手 4:7, 6, 7
- 选手 5:8, 10, 9
过程:
- 将每个选手的成绩进行排序:
- 选手 1:9, 8, 6,总和为 23
- 选手 2:9, 8, 7,总和为 24
- 选手 3:9, 8, 7,总和为 24
- 选手 4:7, 7, 6,总和为 20
- 选手 5:10, 9, 8,总和为 27
- 按照总成绩降序排序后:
- 选手 5:27
- 选手 2:24
- 选手 3:24
- 选手 1:23
- 选手 4:20
- 输出前三名选手的编号:
5,2,3
输出:
5,2,3
总结
这段 Python 代码通过输入选手的成绩数据,使用自定义的比较函数对选手进行排序,最终输出前 3 名选手的编号。通过 functools.cmp_to_key()
和 sort()
实现了多条件的排序(总成绩和单项成绩)。代码结构清晰,采用了 Python 的高效排序和映射功能。
五、C/C++算法源码:
下面我将把给定的 Python 代码转换成 C++ 和 C 代码,并为每个部分添加详细的中文注释和讲解。
C++ 代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
using namespace std;
// 比较函数,用于排序
bool cmp(const pair<int, pair<int, vector<int>>>& a, const pair<int, pair<int, vector<int>>>& b) {
// 如果总分不同,按总分降序排列
if (a.second.first != b.second.first)
return a.second.first > b.second.first;
// 如果总分相同,按逐个成绩降序排列
for (int i = 0; i < a.second.second.size(); i++) {
if (a.second.second[i] != b.second.second[i])
return a.second.second[i] > b.second.second[i];
}
return false;
}
// 获取结果的函数
string getResult(int m, int n, vector<vector<int>>& scores) {
if (m < 3 || m > 10 || n < 3 || n > 100) {
return "-1";
}
// 存储选手的成绩和总分
vector<pair<int, pair<int, vector<int>>>> players;
for (int j = 0; j < n; j++) {
vector<int> player;
int total = 0;
// 获取每个选手的成绩
for (int i = 0; i < m; i++) {
int score = scores[i][j];
if (score < 1 || score > 10) {
return "-1"; // 如果成绩不合法,返回-1
}
player.push_back(score);
total += score;
}
sort(player.rbegin(), player.rend()); // 按成绩降序排列
players.push_back(make_pair(j, make_pair(total, player)));
}
// 按照总分和逐个成绩进行排序
sort(players.begin(), players.end(), cmp);
// 取前三名选手,拼接编号并返回
stringstream result;
for (int i = 0; i < 3; i++) {
result << players[i].first + 1;
if (i < 2) result << ","; // 逗号分隔
}
return result.str();
}
int main() {
int m, n;
cin >> m >> n;
vector<vector<int>> scores(m, vector<int>(n));
// 输入成绩数据
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
cin >> scores[i][j];
}
}
// 输出结果
cout << getResult(m, n, scores) << endl;
return 0;
}
C++ 代码解析
-
输入部分
int m, n; cin >> m >> n; vector<vector<int>> scores(m, vector<int>(n));
- 通过
cin >> m >> n;
获取输入的m
和n
(比赛数和选手数)。 - 使用
vector<vector<int>>
来存储每个选手的成绩数据。
- 通过
-
获取选手成绩
for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { cin >> scores[i][j]; } }
- 使用嵌套的
for
循环遍历输入的每个成绩,并将它们存储在scores
数组中。
- 使用嵌套的
-
排序比较函数
cmp
bool cmp(const pair<int, pair<int, vector<int>>>& a, const pair<int, pair<int, vector<int>>>& b) { if (a.second.first != b.second.first) return a.second.first > b.second.first; for (int i = 0; i < a.second.second.size(); i++) { if (a.second.second[i] != b.second.second[i]) return a.second.second[i] > b.second.second[i]; } return false; }
cmp
函数按照选手的总分进行降序排列。如果总分相同,再按照每个比赛的成绩逐项比较,确保按成绩顺序排列。
-
主逻辑函数
getResult
string getResult(int m, int n, vector<vector<int>>& scores) { if (m < 3 || m > 10 || n < 3 || n > 100) { return "-1"; } vector<pair<int, pair<int, vector<int>>>> players; for (int j = 0; j < n; j++) { vector<int> player; int total = 0; for (int i = 0; i < m; i++) { int score = scores[i][j]; if (score < 1 || score > 10) { return "-1"; } player.push_back(score); total += score; } sort(player.rbegin(), player.rend()); players.push_back(make_pair(j, make_pair(total, player))); } sort(players.begin(), players.end(), cmp); stringstream result; for (int i = 0; i < 3; i++) { result << players[i].first + 1; if (i < 2) result << ","; } return result.str(); }
- 该函数处理成绩输入,检查成绩合法性,排序并输出前三名选手的编号。
- 通过
sort
按照总分和逐项成绩排序,并通过stringstream
拼接最终结果。
-
输出部分
cout << getResult(m, n, scores) << endl;
C 代码
#include <stdio.h>
#include <stdlib.h>
#define MAX_M 10
#define MAX_N 100
// 选手信息结构体
typedef struct {
int idx; // 选手编号
int sum; // 总分
int score[MAX_M]; // 成绩数组
} Player;
// 比较函数,用于排序
int cmp(const void *a, const void *b) {
Player *playerA = (Player *)a;
Player *playerB = (Player *)b;
if (playerA->sum != playerB->sum)
return playerB->sum - playerA->sum; // 总分降序
for (int i = 0; i < MAX_M; i++) {
if (playerA->score[i] != playerB->score[i])
return playerB->score[i] - playerA->score[i]; // 逐项成绩降序
}
return 0;
}
// 获取结果的函数
char* getResult(int m, int n, int scores[MAX_M][MAX_N]) {
static char result[100]; // 用于存储返回的结果
if (m < 3 || m > 10 || n < 3 || n > 100) {
return "-1";
}
Player players[MAX_N];
int playerCount = 0;
for (int j = 0; j < n; j++) {
Player player;
player.idx = j;
player.sum = 0;
for (int i = 0; i < m; i++) {
int score = scores[i][j];
if (score < 1 || score > 10) {
return "-1"; // 如果成绩不合法,返回-1
}
player.score[i] = score;
player.sum += score;
}
// 对选手成绩排序
qsort(player.score, m, sizeof(int), (int (*)(const void *, const void *))cmp);
players[playerCount++] = player;
}
// 按照总分和成绩逐项排序
qsort(players, playerCount, sizeof(Player), cmp);
// 生成前三名的编号结果
char temp[10];
result[0] = '\0'; // 初始化结果字符串
for (int i = 0; i < 3; i++) {
sprintf(temp, "%d", players[i].idx + 1); // 编号从1开始
strcat(result, temp);
if (i < 2) strcat(result, ",");
}
return result;
}
int main() {
int m, n;
int scores[MAX_M][MAX_N];
// 输入
scanf("%d %d", &m, &n);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &scores[i][j]);
}
}
// 输出结果
printf("%s\n", getResult(m, n, scores));
return 0;
}
C 代码解析
-
输入部分
scanf("%d %d", &m, &n); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { scanf("%d", &scores[i][j]); } }
- 使用
scanf
从标准输入获取比赛数m
和选手数n
。 - 接着,使用嵌套的
for
循环读取每个选手在每场比赛中的成绩,并将它们存储在scores
数组中,scores[i][j]
表示第i
场比赛中第j
选手的成绩。
- 使用
-
选手信息结构体
Player
typedef struct { int idx; // 选手编号 int sum; // 总分 int score[MAX_M]; // 成绩数组 } Player;
- 定义了一个结构体
Player
,用来存储每个选手的信息。结构体包含:idx
:选手的编号(从 0 开始,最后会输出时加 1)。sum
:该选手的总分。score[MAX_M]
:该选手的成绩数组,存储该选手在每场比赛中的成绩。
- 定义了一个结构体
-
比较函数
cmp
int cmp(const void *a, const void *b) { Player *playerA = (Player *)a; Player *playerB = (Player *)b; if (playerA->sum != playerB->sum) return playerB->sum - playerA->sum; // 总分降序 for (int i = 0; i < MAX_M; i++) { if (playerA->score[i] != playerB->score[i]) return playerB->score[i] - playerA->score[i]; // 逐项成绩降序 } return 0; }
- 使用
qsort
对选手进行排序时,需要提供一个比较函数cmp
。 cmp
比较两个选手的总分sum
,如果总分不同,按总分降序排列。如果总分相同,再逐个比较成绩数组,按每项成绩降序排序。
- 使用
-
获取结果函数
getResult
char* getResult(int m, int n, int scores[MAX_M][MAX_N]) { static char result[100]; // 用于存储返回的结果 if (m < 3 || m > 10 || n < 3 || n > 100) { return "-1"; } Player players[MAX_N]; int playerCount = 0; for (int j = 0; j < n; j++) { Player player; player.idx = j; player.sum = 0; for (int i = 0; i < m; i++) { int score = scores[i][j]; if (score < 1 || score > 10) { return "-1"; // 如果成绩不合法,返回-1 } player.score[i] = score; player.sum += score; } qsort(player.score, m, sizeof(int), (int (*)(const void *, const void *))cmp); players[playerCount++] = player; } qsort(players, playerCount, sizeof(Player), cmp); char temp[10]; result[0] = '\0'; // 初始化结果字符串 for (int i = 0; i < 3; i++) { sprintf(temp, "%d", players[i].idx + 1); // 编号从1开始 strcat(result, temp); if (i < 2) strcat(result, ","); } return result; }
getResult
函数负责处理所有逻辑:- 合法性检查:如果
m
或n
不在指定范围内(3 ≤ m ≤ 10 和 3 ≤ n ≤ 100),返回"-1"
。 - 选手成绩处理:
- 为每个选手(
j
)构造一个Player
结构体,存储选手的成绩和总分。 - 检查每场比赛的成绩是否合法(1 ≤ score ≤ 10),如果有不合法的成绩,立即返回
"-1"
。 - 对每个选手的成绩按降序排序。
- 为每个选手(
- 排序:使用
qsort
对所有选手按总分和逐项成绩进行排序。 - 生成输出结果:将前三名选手的编号(从 1 开始)拼接成一个逗号分隔的字符串。
- 合法性检查:如果
-
主函数
main
int main() { int m, n; int scores[MAX_M][MAX_N]; // 输入 scanf("%d %d", &m, &n); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { scanf("%d", &scores[i][j]); } } // 输出结果 printf("%s\n", getResult(m, n, scores)); return 0; }
main
函数负责输入数据,并调用getResult
输出前三名选手的编号。- 使用
scanf
获取输入的m
、n
和scores
数组。 - 最后输出
getResult
函数的返回值,显示结果。
总结
- 这段 C 代码与 C++ 代码的结构大体相似,都通过数组和结构体来存储选手的信息,并使用排序来处理成绩。
- C 代码中使用
qsort
进行排序,并通过自定义的比较函数cmp
来控制排序的逻辑。getResult
函数负责处理输入数据、排序和生成输出结果。 - 代码中的
Player
结构体用于存储每个选手的成绩信息,确保可以方便地对选手进行排序。
C 和 C++ 都有其强大的排序功能,但 C++ 通过 std::sort
和 lambda
表达式提供了更简洁的排序方式,而 C 使用了 qsort
和自定义的比较函数实现排序。
六、尾言
什么是华为OD?
华为OD(Outsourcing Developer,外包开发工程师)是华为针对软件开发工程师岗位的一种招聘形式,主要包括笔试、技术面试以及综合面试等环节。尤其在笔试部分,算法题的机试至关重要。
为什么刷题很重要?
-
机试是进入技术面的第一关:
华为OD机试(常被称为机考)主要考察算法和编程能力。只有通过机试,才能进入后续的技术面试环节。 -
技术面试需要手撕代码:
技术一面和二面通常会涉及现场编写代码或算法题。面试官会注重考察候选人的思路清晰度、代码规范性以及解决问题的能力。因此提前刷题、多练习是通过面试的重要保障。 -
入职后的可信考试:
入职华为后,还需要通过“可信考试”。可信考试分为三个等级:- 入门级:主要考察基础算法与编程能力。
- 工作级:更贴近实际业务需求,可能涉及复杂的算法或与工作内容相关的场景题目。
- 专业级:最高等级,考察深层次的算法以及优化能力,与薪资直接挂钩。
刷题策略与说明:
2024年8月14日之后,华为OD机试的题库转为 E卷,由往年题库(D卷、A卷、B卷、C卷)和全新题目组成。刷题时可以参考以下策略:
-
关注历年真题:
- 题库中的旧题占比较大,建议优先刷历年的A卷、B卷、C卷、D卷题目。
- 对于每道题目,建议深度理解其解题思路、代码实现,以及相关算法的适用场景。
-
适应新题目:
- E卷中包含全新题目,需要掌握全面的算法知识和一定的灵活应对能力。
- 建议关注新的刷题平台或交流群,获取最新题目的解析和动态。
-
掌握常见算法:
华为OD考试通常涉及以下算法和数据结构:- 排序算法(快速排序、归并排序等)
- 动态规划(背包问题、最长公共子序列等)
- 贪心算法
- 栈、队列、链表的操作
- 图论(最短路径、最小生成树等)
- 滑动窗口、双指针算法
-
保持编程规范:
- 注重代码的可读性和注释的清晰度。
- 熟练使用常见编程语言,如C++、Java、Python等。
如何获取资源?
-
官方参考:
- 华为招聘官网或相关的招聘平台会有一些参考信息。
- 华为OD的相关公众号可能也会发布相关的刷题资料或学习资源。
-
加入刷题社区:
- 找到可信的刷题交流群,与其他备考的小伙伴交流经验。
- 关注知名的刷题网站,如LeetCode、牛客网等,这些平台上有许多华为OD的历年真题和解析。
-
寻找系统性的教程:
- 学习一本经典的算法书籍,例如《算法导论》《剑指Offer》《编程之美》等。
- 完成系统的学习课程,例如数据结构与算法的在线课程。
积极心态与持续努力:
刷题的过程可能会比较枯燥,但它能够显著提升编程能力和算法思维。无论是为了通过华为OD的招聘考试,还是为了未来的职业发展,这些积累都会成为重要的财富。
考试注意细节
-
本地编写代码
- 在本地 IDE(如 VS Code、PyCharm 等)上编写、保存和调试代码,确保逻辑正确后再复制粘贴到考试页面。这样可以减少语法错误,提高代码准确性。
-
调整心态,保持冷静
- 遇到提示不足或实现不确定的问题时,不必慌张,可以采用更简单或更有把握的方法替代,确保思路清晰。
-
输入输出完整性
- 注意训练和考试时都需要编写完整的输入输出代码,尤其是和题目示例保持一致。完成代码后务必及时调试,确保功能符合要求。
-
快捷键使用
- 删除行可用
Ctrl+D
,复制、粘贴和撤销分别为Ctrl+C
,Ctrl+V
,Ctrl+Z
,这些可以正常使用。 - 避免使用
Ctrl+S
,以免触发浏览器的保存功能。
- 删除行可用
-
浏览器要求
- 使用最新版的 Google Chrome 浏览器完成考试,确保摄像头开启并正常工作。考试期间不要切换到其他网站,以免影响考试成绩。
-
交卷相关
- 答题前,务必仔细查看题目示例,避免遗漏要求。
- 每完成一道题后,点击【保存并调试】按钮,多次保存和调试是允许的,系统会记录得分最高的一次结果。完成所有题目后,点击【提交本题型】按钮。
- 确保在考试结束前提交试卷,避免因未保存或调试失误而丢分。
-
时间和分数安排
- 总时间:150 分钟;总分:400 分。
- 试卷结构:2 道一星难度题(每题 100 分),1 道二星难度题(200 分)。及格分为 150 分。合理分配时间,优先完成自己擅长的题目。
-
考试环境准备
- 考试前请备好草稿纸和笔。考试中尽量避免离开座位,确保监控画面正常。
- 如需上厕所,请提前规划好时间以减少中途离开监控的可能性。
-
技术问题处理
- 如果考试中遇到断电、断网、死机等技术问题,可以关闭浏览器并重新打开试卷链接继续作答。
- 出现其他问题,请第一时间联系 HR 或监考人员进行反馈。
祝你考试顺利,取得理想成绩!