比赛的冠亚季军

一、问题描述

        有N(3 ≤ N < 10000)个运动员,他们的id为0到N-1,他们的实力由一组整数表示。他们之间进行比赛,需要决出冠亚军。比赛的规则是0号和1号比赛,2号和3号比赛,以此类推,每一轮,相邻的运动员进行比赛,获胜的进入下一轮;实力值大的获胜,实力值相等的情况,id小的情况下获胜;轮空的直接进入下一轮。

        比赛规则总结如下:

  1. 初始轮次中,0 号与 1 号比赛、2 号与 3 号比赛,以此类推,相邻运动员两两对决
  2. 每一轮比赛,实力值大的选手获胜;若实力值相等,id 更小的选手获胜
  3. 轮空的选手(当当前轮次人数为奇数时,最后一位选手)直接进入下一轮

二、输入输出描述

输入描述

  • 输入一行N个数字代表N的运动员的实力值(0<=实力值<=10000000000)。

输出描述

  • 输出冠亚季军的id,用空格隔开

三、示例

输入2 3 4 5
输出3 1 2
说明

第一轮比赛,

id为0实力值为2的运动员和id为1实力值为3的运动员比赛,1号胜出进入下一轮争夺冠亚军,

id为2的运动员和id为3的运动员比赛,3号胜出进入下一轮争夺冠亚军,

冠亚军比赛,3号胜1号,

故冠军为3号,亚军为1号,2号与0号,比赛进行季军的争夺,2号实力值为4,0号实力值2,故2号胜出,得季军。冠亚季军为3 1 2。

四、解题思路

1. 核心思想

通过 “多轮两两晋级赛” 筛选强者,同时保留最近 3 轮的失败者队列(仅这部分人有资格竞争季军),最终通过队列分层确定冠、亚、季军 —— 避免全量排序,用 “淘汰制 + 队列缓存” 高效锁定获奖候选人。

2. 问题本质分析

题目本质是 “从无序集合中筛选 Top3”,但附加了特殊对决规则(序号小者在实力相等 / 劣势时获胜),且需避免冗余计算:

  • 冠军:需通过多轮晋级赛决出(只有持续获胜到最后 1 人的才是冠军)。
  • 亚军:只能是 “最后一轮输给冠军的人”(即冠军组的上一轮失败组)。
  • 季军:只能是 “倒数第二轮的失败者”(即冠军组的上两轮失败组)—— 更早被淘汰的人实力不足以竞争季军,无需保留。
3. 核心逻辑
  • 晋级机制:两两对决,按规则产生获胜组(晋级下一轮)和失败组(进入候选队列),奇数个时末尾直接晋级。
  • 队列缓存:用链表维护最近 3 轮的组(头部是最新获胜组,尾部是更早的失败组),超过 3 组则剔除尾部(无缘季军)。
  • 冠军锁定:循环晋级头部获胜组,直到其仅含 1 人(冠军)。
  • 亚、季军锁定:队列第 2 组的唯一者为亚军,第 3 组排序后取最优者为季军。
4. 步骤拆解
  1. 输入解析与对象封装

    • 读取输入的实力数组,为每个元素绑定编号(索引),封装为Sport对象列表。
  2. 初始晋级赛

    • 调用promote方法,对所有运动员执行第一轮两两对决,产生初始的获胜组和失败组,存入结果队列(队列仅保留 3 组)。
  3. 循环晋级决出冠军

    • 若队列头部的获胜组人数 > 1,说明冠军未决出,移除该获胜组再次执行promote,将新的获胜组 / 失败组压入队列头部。
    • 重复上述步骤,直到头部获胜组仅含 1 人(即冠军)。
  4. 确定亚、季军

    • 亚军:队列第 2 组(上一轮输给冠军的失败组)的唯一候选人。
    • 季军:队列第 3 组(上两轮的失败组)按 “实力降序 + 编号升序” 排序,取第 1 名。
  5. 结果输出

    • 拼接冠军、亚军、季军的编号,返回最终结果。

五、代码实现

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {
  // 运动员类
  static class Sport {
    int id; // 运动员的id
    long strength; // 运动员的实力

    public Sport(int id, long strength) {
      this.id = id;
      this.strength = strength;
    }
  }

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    long[] strengths = Arrays.stream(sc.nextLine().split(" ")).mapToLong(Long::parseLong).toArray();

    System.out.println(getResult(strengths));
  }

  public static String getResult(long[] strength) {
    // ans只记录三个组,冠军组,亚军组,季军组
    LinkedList<ArrayList<Sport>> ans = new LinkedList<>();

    // 将输入的实力值,转化为运动员集合
    ArrayList<Sport> sports = new ArrayList<>();
    for (int i = 0; i < strength.length; i++) sports.add(new Sport(i, strength[i]));

    // 晋级赛
    promote(sports, ans);

    // 冠军组如果不是一个人,那么还需要取出冠军组继续进行晋级赛
    while (ans.getFirst().size() > 1) {
      promote(ans.removeFirst(), ans);
    }

    // 冠军
    int first = ans.get(0).get(0).id;

    // 亚军
    int second = ans.get(1).get(0).id;

    // 季军
    ans.get(2)
        .sort(
            (a, b) ->
                a.strength != b.strength ? b.strength - a.strength > 0 ? 1 : -1 : a.id - b.id);
    int third = ans.get(2).get(0).id;

    return first + " " + second + " " + third;
  }

  public static void promote(ArrayList<Sport> sports, LinkedList<ArrayList<Sport>> ans) {
    // 记录获胜组
    ArrayList<Sport> win = new ArrayList<>();
    // 记录失败组
    ArrayList<Sport> fail = new ArrayList<>();

    for (int i = 1; i < sports.size(); i += 2) {
      // 序号大的运动员
      Sport major = sports.get(i);
      // 序号小的运动员
      Sport minor = sports.get(i - 1);

      if (major.strength > minor.strength) {
        win.add(major);
        fail.add(minor);
      } else {
        // 如果序号大的运动员的实力 <= 序号小的运动员,则序号小的运动员获胜
        win.add(minor);
        fail.add(major);
      }
    }

    // 如果晋级赛中运动员个数是奇数个,那么最后一个运动员直接晋级
    if (sports.size() % 2 != 0) {
      win.add(sports.get(sports.size() - 1));
    }

    // 依次头部压入失败组,获胜组,保证头部是获胜组
    ans.addFirst(fail);
    ans.addFirst(win);

    // 如果保留组个数超过3个,那么需要将超过部分的组去掉,因为这部分人已经无缘季军
    while (ans.size() > 3) ans.removeLast();
  }
}

### Java 实现比赛排名冠亚季军逻辑 以下是基于引用内容设计的一个完整的 Java 解决方案,用于实现比赛冠亚季军排名逻辑。该解决方案遵循递归方式模拟比赛过程,并通过合理的数据结构和排序机制确保程序的效率与准确性。 #### 代码实现 ```java import java.util.*; class Sman implements Comparable<Sman> { int id; long strength; public Sman(int id, long strength) { this.id = id; this.strength = strength; } @Override public int compareTo(Sman other) { if (this.strength != other.strength) { return Long.compare(other.strength, this.strength); // 按实力降序排列 } else { return Integer.compare(this.id, other.id); // 若实力相同按 ID 升序排列 } } } public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); String[] inputStrengths = scanner.nextLine().split(" "); List<Sman> athletes = new ArrayList<>(); for (int i = 0; i < inputStrengths.length; i++) { athletes.add(new Sman(i, Long.parseLong(inputStrengths[i]))); } Collections.sort(athletes); List<Integer> winners = new ArrayList<>(); List<Integer> losers = new ArrayList<>(); solveMethod(athletes, winners, losers); System.out.println(winners.get(0) + " " + winners.get(1) + " " + determineThirdPlace(losers)); } private static void solveMethod(List<Sman> currentRound, List<Integer> winners, List<Integer> losers) { if (currentRound.size() <= 2) { // 当前轮次只剩两位选手时结束递归 for (Sman s : currentRound) { winners.add(s.id); } return; } List<Sman> nextRound = new ArrayList<>(); for (int i = 0; i < currentRound.size(); i += 2) { if (i + 1 >= currentRound.size()) { // 轮空情况 nextRound.add(currentRound.get(i)); continue; } Sman a = currentRound.get(i); Sman b = currentRound.get(i + 1); if (a.compareTo(b) <= 0) { // 判断胜负并分配至下一阶段 nextRound.add(b); losers.add(a.id); } else { nextRound.add(a); losers.add(b.id); } } solveMethod(nextRound, winners, losers); } private static int determineThirdPlace(List<Integer> losers) { losers.sort((o1, o2) -> { if (!Objects.equals(Main.inputStrengths[o1], Main.inputStrengths[o2])) { return Long.compare( Long.parseLong(Main.inputStrengths[o2]), Long.parseLong(Main.inputStrengths[o1]) ); } else { return Integer.compare(o1, o2); } }); return losers.get(0); // 返回最强的失败者作为季军 } static String[] inputStrengths; // 静态变量保存原始输入以便后续使用 } ``` --- #### 关键点解析 1. **数据建模** 使用 `Sman` 类封装运动员的信息(ID 和实力),并通过实现 `Comparable<Sman>` 接口定义比较规则:先按照实力降序排列;若实力相等,则按 ID 升序排列[^1]。 2. **递归逻辑** 函数 `solveMethod` 模拟比赛过程。每一轮比赛中,相邻两个运动员进行对比,胜者进入下一轮,败者被记录到失败队列中。当当前轮次仅剩两名或更少选手时停止递归[^3]。 3. **结果计算** - 冠军和亚军直接从胜利队列中获取。 - 季军通过分析失败队列中的成员得出,需重新排序以找到最强大的失败者[^4]。 4. **时间复杂度与空间复杂度** 时间复杂度主要取决于递归层数以及每次排序操作的时间消耗,整体为 \(O(N \log N)\)[^1]。由于每一层递归都需要创建新的列表存储参赛者信息,因此空间复杂度为 \(O(N)\)[^1]。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值