射击比赛

爱笑的小胡纸 / 2023-08-10 / 原文

1. 题目

给定一个射击比赛成绩单
包含多个选手若干次射击的成绩分数
请对每个选手按其最高三个分数之和进行降序排名
输出降序排名后的选手ID序列

条件如下:

  1. 一个选手可以有多个射击成绩的分数 且次序不固定
  2. 如果一个选手成绩小于三个 则认为选手的所有成绩无效 排名忽略该选手
  3. 如果选手的成绩之和相等,则成绩相等的选手按照其ID降序排列

输入描述
输入第一行:一个整数 N
表示该场比赛总共进行了N次射击
产生N个成绩分数 2 <= N <= 100
输入第二行 一个长度为N的整数序列
表示参与本次射击的选手Id
0 <= ID <= 99
输入第三行是长度为N的整数序列
表示参与每次射击的选手对应的成绩
0 <= 成绩 <= 100

输出描述
符合题设条件的降序排名后的选手ID序列

输入例子:
13
3,3,7,4,4,4,4,7,7,3,5,5,5
53,80,68,24,39,76,66,16,100,55,53,80,55
输出结果:
5,3,7,4
说明:
该场射击比赛进行了13次,参赛选手为3 4 5 7
3号选手的成绩为53 80 55最高三个成绩的和为 188
4号选手的成绩为24 39 76 66最高三个和为181
5号选手的成绩为53 80 55 最高三个和为188
7号选手成绩为68 16 100 最高三个和184
比较各个选手最高三个成绩的和
3 = 5 > 7 > 4
由于3和5成绩相等 且5 > 3 所以输出为5,3,7,4

2. 答案

class Test {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNextLine()) {
            int n = in.nextInt();
            in.nextLine();
            String idStr = in.nextLine();
            String scoreStr = in.nextLine();
            String[] ids = idStr.split(",");
            String[] scores = scoreStr.split(",");
            Map<Integer, List<Integer>> map = new HashMap<>();
            for (int i = 0; i < n; i++) {
                int key = Integer.parseInt(ids[i]);
                int value = Integer.parseInt(scores[i]);
                if (map.containsKey(key)) {
                    map.get(key).add(value);
                } else {
                    List<Integer> list = new ArrayList<>();
                    list.add(value);
                    map.put(key, list);
                }
            }
            solutionStream(map);
        }
    }

    /**
     * Stream 翻译称为 “流”,是 Java8 的新特性之一。 Stream 将要处理的元素看作是流,这时可以借助 Stream API 对流中的元素进行中间操作,比如:筛选、排序、排序等
     *
     * 1.创建Stream : map.entrySet().stream()
     * 2.中间操作
     *  filter: 过滤流,过滤流中的元素,返回一个符合条件的Stream
     *  sorted: 返回一个排序的Stream
     *  limit: 返回前n个元素数据组成的Stream。属于短路操作
     *  map: 转换流,将一种类型的流转换为另外一种流(mapToInt、mapToLong、mapToDouble 返回int、long、double基本类型对应的Stream)
     * 3.终止操作
     *  collect: 聚合操作,封装目标数据,将流转换为其他形式接收,如: List、Set、Map、Array
     *
     * Java 8 流的新类 java.util.stream.Collectors 实现了 java.util.stream.Collector 接口,同时又提供了大量的方法对流 ( stream ) 的元素执行 map and reduce 操作,或者统计操作
     * Collectors.joining() 方法用某个指定的拼接**字符串**把所有元素拼接成一个字符串,并添加可选的前缀和后缀
     *
     * @param map key是选手的id,value是选手的成绩集合
     */
    private static void solutionStream(Map<Integer, List<Integer>> map) {
        String collectStr = map.entrySet().stream()
                .filter(per -> (per.getValue().size() >= 3)) // 成绩少于3个成绩无效
                .sorted((p1, p2) -> {
                    // 成绩降序→取前三→求和
                    int sum1 = p1.getValue().stream().sorted(Comparator.reverseOrder()).limit(3).mapToInt(Integer::valueOf).sum();
                    int sum2 = p2.getValue().stream().sorted(Comparator.reverseOrder()).limit(3).mapToInt(Integer::valueOf).sum();
                    // 最大成绩降序前三加和降序,加和相同的话Id降序
                    if (sum1 == sum2) {
                        return p2.getKey() - p1.getKey();
                    } else {
                        return sum2 - sum1;
                    }
                })
                .map(Map.Entry::getKey) // 取得员工Id
                .map(String::valueOf) // 类型不转没办法joining
                .collect(Collectors.joining(",")); // 拼接字符串
        System.out.println(collectStr);
    }
}

输出结果: