可上 欧弟OJ系统 练习华子OD、大厂真题
绿色聊天软件戳 od1441了解算法冲刺训练(备注【CSDN】否则不通过)

相关推荐阅读

题目练习网址:【排序】双机位A-比赛

题目描述与示例

题目描述

一个有N个选手参加比赛,选手编号为1~N3<=N<=100),有M3<=M<=10)个评委对选手进行打分。

打分规则为每个评委对选手打分,最高分10分,最低分1分。

请计算得分最多的3位选手的编号。

如果得分相同,则得分高分值最多的选手排名靠前

(10分数量相同,则比较9分的数量,以此类推,用例中不会出现多个选手得分完全相同的情况)。

输入描述

第一行为半角逗号分割的两个正整数,第一个数字表示M3<=M<=10)个评委,第二个数字表示N3<=N<=100)个选手。

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

输出描述

选手前3名的编号。

注:若输入为异常,输出-1,如MN、打分不在范围内。

示例一

输入

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分排第11号选手36分排第25号选手30分(210分值有3个,110分值只有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,无效分数

解题思路

比较典型的排序题,先按照分数总和进行逆序排序,但由于在分数相同的时候要依次比较分数101的分数,如果直接用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)])

剩下的主要问题就是处理异常了。

异常分为两种

  1. 第一种是类似示例二三四所示,给出的数据不位于指定范围内
  2. 第二种是输入的格式有问题,譬如行数列数不够、分割符并非逗号、输入包含字母等无关字符等等

第一种错误是可以通过条件语句来排除掉的,但第二种错误因为错误种类繁杂, 我们也无法提前知道考试时候的具体测试用例中包含哪些错误,因此需要使用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或了解更多

更多推荐