当前位置: 首页 > article >正文

【2024年华为OD机试】(B卷,100分)- 比赛 (Java JS PythonC/C++)

在这里插入图片描述

一、问题描述

题目解析

题目描述

一个有 N 个选手参加比赛,选手编号为 1~N3 <= N <= 100),有 M 个评委对选手进行打分(3 <= M <= 10)。每个评委对选手打分的范围为 110 分。请计算得分最多的 3 位选手的编号。如果得分相同,则得分高分值最多的选手排名靠前(即比较 10 分的数量,如果相同则比较 9 分的数量,以此类推)。

输入描述

  1. 第一行为半角逗号分割的两个正整数,第一个数字表示 M 个评委,第二个数字表示 N 个选手。
  2. 第 2 到 M+1 行是半角逗号分割的整数序列,表示评委为每个选手的打分,0 号下标数字表示 1 号选手分数,1 号下标数字表示 2 号选手分数,依次类推。

输出描述

输出选手前 3 名的编号。如果输入异常(如 MN、打分不在范围内),则输出 -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,无效分数。


题目解析

问题分析

  1. 目标
    根据评委的打分,计算每个选手的总得分,并按照得分和高分值数量排序,输出前 3 名选手的编号。

  2. 关键点

    • 每个选手的总得分为所有评委打分的总和。
    • 如果总得分相同,则比较 10 分的数量,如果相同则比较 9 分的数量,以此类推。
    • 需要处理输入异常情况(如 MN 不在范围内,或打分不在 110 分之间)。
  3. 问题转化
    通过统计每个选手的总得分和高分值数量,按照规则排序,输出前 3 名。


解题思路

  1. 输入检查

    • 检查 MN 是否在有效范围内(3 <= M <= 103 <= N <= 100)。
    • 检查每个评委的打分是否在 110 分之间。
  2. 统计得分和高分值数量

    • 对于每个选手,计算总得分。
    • 统计每个选手的 10 分、9 分等高分值的数量。
  3. 排序规则

    • 按照总得分从高到低排序。
    • 如果总得分相同,则按照 10 分数量从高到低排序,如果 10 分数量相同,则比较 9 分数量,以此类推。
  4. 输出结果

    • 输出前 3 名选手的编号。

示例分析

输入

4,5
10,6,9,7,6
9,10,6,7,5
8,10,6,5,10
9,10,8,4,9

步骤

  1. 检查输入:
    • M = 4N = 5,符合范围。
    • 所有打分在 110 分之间。
  2. 统计每个选手的总得分和高分值数量:
    • 1 号选手:总得分 10 + 9 + 8 + 9 = 3610 分数量 1
    • 2 号选手:总得分 6 + 10 + 10 + 10 = 3610 分数量 3
    • 3 号选手:总得分 9 + 6 + 6 + 8 = 2910 分数量 0
    • 4 号选手:总得分 7 + 7 + 5 + 4 = 2310 分数量 0
    • 5 号选手:总得分 6 + 5 + 10 + 9 = 3010 分数量 1
  3. 排序:
    • 2 号选手(36 分,10 分数量 3)。
    • 1 号选手(36 分,10 分数量 1)。
    • 5 号选手(30 分,10 分数量 1)。
  4. 输出结果:2,1,5

复杂度分析

  1. 时间复杂度

    • 输入检查:O(M * N)
    • 统计得分和高分值数量:O(M * N)
    • 排序:O(N log N)
    • 总时间复杂度:O(M * N + N log N)
  2. 空间复杂度

    • 存储每个选手的得分和高分值数量:O(N)
    • 总空间复杂度:O(N)

总结

本题的核心是通过统计每个选手的总得分和高分值数量,按照规则排序,输出前 3 名选手的编号。关键在于:

  1. 检查输入合法性。
  2. 统计每个选手的得分和高分值数量。
  3. 按照总得分和高分值数量排序。
  4. 时间复杂度为 O(M * N + N log N),适合处理 MN 较小的输入。

二、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) => {...}):每当输入一行时,这个回调函数会被调用。通过它,我们可以读取并处理每一行输入。
    • 第一行输入包含了 mn(用逗号隔开)。
    • 后续的 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:选手的数量。
  • 处理过程

    • 首先,检查 mn 是否符合规定的范围。如果不符合,直接返回 -1
    • 对每个选手的成绩进行处理:
      • 如果成绩不在合理范围(1 到 10),返回 -1
      • 将每个选手的成绩降序排序。
      • 计算每个选手的成绩总和,并保存选手的排序信息(包括总和和排序后的成绩)。
    • 对所有选手按总成绩进行降序排序。如果总成绩相同,则逐项比较成绩,按成绩降序排序。
    • 最后,选出前三名选手,返回他们的编号(加1是因为编号从1开始)。
3. rl.on("line") 的处理流程
  • 第一行输入包含两个整数 mn,分别表示成绩条目数和选手数量。
  • 后续的 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. 将每个选手的成绩进行排序:
    • 选手 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
  2. 按照总成绩降序排序后:
    • 选手 5:27
    • 选手 2:24
    • 选手 3:24
    • 选手 1:23
    • 选手 4:20
  3. 输出前三名选手的编号:5,2,3
输出:
5,2,3

总结

这段 JavaScript 代码完成了以下功能:

  1. 通过 ACM 模式获取多行输入。
  2. 对每个选手的成绩进行处理,排序并计算成绩总和。
  3. 按照总成绩(以及若总成绩相同则按具体成绩逐项比较)排序,输出前三名选手的编号。

三、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";

    • 判断输入是否合法。如果 mn 不符合规定的范围(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 进行排序。
    • 排序规则
      • 首先根据每个选手的总成绩(sumAsumB)降序排序。如果总成绩不相同,直接按照总成绩排序。
      • 如果总成绩相同,再按成绩的具体项目进行逐项比较,按每个成绩降序排列。
  • 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() 获取用户输入的第一行,使用逗号(,)分隔后,将其转换为两个整数 mnm 表示比赛的数量(行数),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 函数
    这是一个用于排序的比较函数,接收两个选手的信息 ab,并根据以下规则返回:

    1. 按总分降序排序:如果两个选手的总分 sum 不相同,返回它们的总分差。
    2. 如果总分相同,则按逐项成绩降序排序:逐个比较选手的成绩,如果某个项目的成绩不同,按该项目成绩的差值进行排序。
    3. 若总分和各项成绩都相同,返回 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:
      如果 mn 不在规定的范围内(3 ≤ m ≤ 10,3 ≤ n ≤ 100),函数直接返回 "-1",表示数据不合法。
  • 生成选手数据

    • for j in range(n):
      遍历每个选手(j 为选手的索引),为每个选手生成一个成绩列表。
    • for i in range(m):
      遍历每场比赛的成绩(i 为比赛的索引),将每个选手在各场比赛的成绩保存在 player 列表中。
    • 合法性检查
      • 如果某个成绩不在合法范围(1 到 10 之间),函数返回 "-1"
    • 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. 将每个选手的成绩进行排序:
    • 选手 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
  2. 按照总成绩降序排序后:
    • 选手 5:27
    • 选手 2:24
    • 选手 3:24
    • 选手 1:23
    • 选手 4:20
  3. 输出前三名选手的编号: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++ 代码解析

  1. 输入部分

    int m, n;
    cin >> m >> n;
    vector<vector<int>> scores(m, vector<int>(n));
    
    • 通过 cin >> m >> n; 获取输入的 mn(比赛数和选手数)。
    • 使用 vector<vector<int>> 来存储每个选手的成绩数据。
  2. 获取选手成绩

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cin >> scores[i][j];
        }
    }
    
    • 使用嵌套的 for 循环遍历输入的每个成绩,并将它们存储在 scores 数组中。
  3. 排序比较函数 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 函数按照选手的总分进行降序排列。如果总分相同,再按照每个比赛的成绩逐项比较,确保按成绩顺序排列。
  4. 主逻辑函数 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 拼接最终结果。
  5. 输出部分

    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 代码解析

  1. 输入部分

    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 选手的成绩。
  2. 选手信息结构体 Player

    typedef struct {
        int idx;   // 选手编号
        int sum;   // 总分
        int score[MAX_M]; // 成绩数组
    } Player;
    
    • 定义了一个结构体 Player,用来存储每个选手的信息。结构体包含:
      • idx:选手的编号(从 0 开始,最后会输出时加 1)。
      • sum:该选手的总分。
      • score[MAX_M]:该选手的成绩数组,存储该选手在每场比赛中的成绩。
  3. 比较函数 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,如果总分不同,按总分降序排列。如果总分相同,再逐个比较成绩数组,按每项成绩降序排序。
  4. 获取结果函数 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 函数负责处理所有逻辑:
      1. 合法性检查:如果 mn 不在指定范围内(3 ≤ m ≤ 10 和 3 ≤ n ≤ 100),返回 "-1"
      2. 选手成绩处理
        • 为每个选手(j)构造一个 Player 结构体,存储选手的成绩和总分。
        • 检查每场比赛的成绩是否合法(1 ≤ score ≤ 10),如果有不合法的成绩,立即返回 "-1"
        • 对每个选手的成绩按降序排序。
      3. 排序:使用 qsort 对所有选手按总分和逐项成绩进行排序。
      4. 生成输出结果:将前三名选手的编号(从 1 开始)拼接成一个逗号分隔的字符串。
  5. 主函数 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 获取输入的 mnscores 数组。
    • 最后输出 getResult 函数的返回值,显示结果。

总结

  • 这段 C 代码与 C++ 代码的结构大体相似,都通过数组和结构体来存储选手的信息,并使用排序来处理成绩。
  • C 代码中使用 qsort 进行排序,并通过自定义的比较函数 cmp 来控制排序的逻辑。getResult 函数负责处理输入数据、排序和生成输出结果。
  • 代码中的 Player 结构体用于存储每个选手的成绩信息,确保可以方便地对选手进行排序。

C 和 C++ 都有其强大的排序功能,但 C++ 通过 std::sortlambda 表达式提供了更简洁的排序方式,而 C 使用了 qsort 和自定义的比较函数实现排序。

六、尾言

什么是华为OD?

华为OD(Outsourcing Developer,外包开发工程师)是华为针对软件开发工程师岗位的一种招聘形式,主要包括笔试、技术面试以及综合面试等环节。尤其在笔试部分,算法题的机试至关重要。

为什么刷题很重要?

  1. 机试是进入技术面的第一关:
    华为OD机试(常被称为机考)主要考察算法和编程能力。只有通过机试,才能进入后续的技术面试环节。

  2. 技术面试需要手撕代码:
    技术一面和二面通常会涉及现场编写代码或算法题。面试官会注重考察候选人的思路清晰度、代码规范性以及解决问题的能力。因此提前刷题、多练习是通过面试的重要保障。

  3. 入职后的可信考试:
    入职华为后,还需要通过“可信考试”。可信考试分为三个等级:

    • 入门级:主要考察基础算法与编程能力。
    • 工作级:更贴近实际业务需求,可能涉及复杂的算法或与工作内容相关的场景题目。
    • 专业级:最高等级,考察深层次的算法以及优化能力,与薪资直接挂钩。

刷题策略与说明:

2024年8月14日之后,华为OD机试的题库转为 E卷,由往年题库(D卷、A卷、B卷、C卷)和全新题目组成。刷题时可以参考以下策略:

  1. 关注历年真题:

    • 题库中的旧题占比较大,建议优先刷历年的A卷、B卷、C卷、D卷题目。
    • 对于每道题目,建议深度理解其解题思路、代码实现,以及相关算法的适用场景。
  2. 适应新题目:

    • E卷中包含全新题目,需要掌握全面的算法知识和一定的灵活应对能力。
    • 建议关注新的刷题平台或交流群,获取最新题目的解析和动态。
  3. 掌握常见算法:
    华为OD考试通常涉及以下算法和数据结构:

    • 排序算法(快速排序、归并排序等)
    • 动态规划(背包问题、最长公共子序列等)
    • 贪心算法
    • 栈、队列、链表的操作
    • 图论(最短路径、最小生成树等)
    • 滑动窗口、双指针算法
  4. 保持编程规范:

    • 注重代码的可读性和注释的清晰度。
    • 熟练使用常见编程语言,如C++、Java、Python等。

如何获取资源?

  1. 官方参考:

    • 华为招聘官网或相关的招聘平台会有一些参考信息。
    • 华为OD的相关公众号可能也会发布相关的刷题资料或学习资源。
  2. 加入刷题社区:

    • 找到可信的刷题交流群,与其他备考的小伙伴交流经验。
    • 关注知名的刷题网站,如LeetCode、牛客网等,这些平台上有许多华为OD的历年真题和解析。
  3. 寻找系统性的教程:

    • 学习一本经典的算法书籍,例如《算法导论》《剑指Offer》《编程之美》等。
    • 完成系统的学习课程,例如数据结构与算法的在线课程。

积极心态与持续努力:

刷题的过程可能会比较枯燥,但它能够显著提升编程能力和算法思维。无论是为了通过华为OD的招聘考试,还是为了未来的职业发展,这些积累都会成为重要的财富。

考试注意细节

  1. 本地编写代码

    • 在本地 IDE(如 VS Code、PyCharm 等)上编写、保存和调试代码,确保逻辑正确后再复制粘贴到考试页面。这样可以减少语法错误,提高代码准确性。
  2. 调整心态,保持冷静

    • 遇到提示不足或实现不确定的问题时,不必慌张,可以采用更简单或更有把握的方法替代,确保思路清晰。
  3. 输入输出完整性

    • 注意训练和考试时都需要编写完整的输入输出代码,尤其是和题目示例保持一致。完成代码后务必及时调试,确保功能符合要求。
  4. 快捷键使用

    • 删除行可用 Ctrl+D,复制、粘贴和撤销分别为 Ctrl+CCtrl+VCtrl+Z,这些可以正常使用。
    • 避免使用 Ctrl+S,以免触发浏览器的保存功能。
  5. 浏览器要求

    • 使用最新版的 Google Chrome 浏览器完成考试,确保摄像头开启并正常工作。考试期间不要切换到其他网站,以免影响考试成绩。
  6. 交卷相关

    • 答题前,务必仔细查看题目示例,避免遗漏要求。
    • 每完成一道题后,点击【保存并调试】按钮,多次保存和调试是允许的,系统会记录得分最高的一次结果。完成所有题目后,点击【提交本题型】按钮。
    • 确保在考试结束前提交试卷,避免因未保存或调试失误而丢分。
  7. 时间和分数安排

    • 总时间:150 分钟;总分:400 分。
    • 试卷结构:2 道一星难度题(每题 100 分),1 道二星难度题(200 分)。及格分为 150 分。合理分配时间,优先完成自己擅长的题目。
  8. 考试环境准备

    • 考试前请备好草稿纸和笔。考试中尽量避免离开座位,确保监控画面正常。
    • 如需上厕所,请提前规划好时间以减少中途离开监控的可能性。
  9. 技术问题处理

    • 如果考试中遇到断电、断网、死机等技术问题,可以关闭浏览器并重新打开试卷链接继续作答。
    • 出现其他问题,请第一时间联系 HR 或监考人员进行反馈。

祝你考试顺利,取得理想成绩!


http://www.kler.cn/a/507916.html

相关文章:

  • Qiankun 微前端框架全面解析:架构、原理与最佳实践
  • 数字小偷:2025年全面防护指南
  • SUN的J2EE与微软的DNA
  • 主链和Layer2之间资产转移
  • Golang结合MySQL和DuckDB提高查询性能
  • 梁山派入门指南4——定时器使用详解,包括定时器中断、PWM产生、输入捕获测量频率
  • 隧道IP广播与紧急电话系统:提升隧道安全的关键技术
  • CanTp 笔记
  • 【微信小程序】5|我的页面 | 我的咖啡店-综合实训
  • 【PowerQuery专栏】PowerQuery 函数之CSV文件处理函数
  • 手机上做笔记的APP工具?有哪些好用的记笔记APP
  • 警惕IDEA 2024版重大Bug问题:LomBok失效、Gradle冲突、Spring Boot启动错误
  • 【Azure 架构师学习笔记】- Azure Function (2) --实操1
  • JVM直击重点
  • 在 Azure 100 学生订阅中新建 Ubuntu VPS 并通过 Docker 部署 pSQL 服务器
  • 加菲工具格式化XML:让数据呈现更清晰
  • Python 文字生成语言,保存为wav格式
  • SQL2000在win10上安装的方法
  • go语言zero框架中在线截图chromedp 设置超限的网页长度
  • 基于matlab的火焰高度求解
  • docker与部署微服务实战
  • Elasticsearch单机安装
  • 重新审视端到端传输协议:从观念到原则
  • Python 字符串分割时 spilt 和 re 效率对比
  • 2021年前端部署的灵魂拷问
  • 快速入门:如何注册并使用GPT