【2024年华为OD机试】 (A卷,100分)- 插队 (Java & JS & Python&C/C++)

一、问题描述

题目描述

某银行将客户分为了若干个优先级,1 级最高,5 级最低。当你需要在银行办理业务时,优先级高的人随时可以插队到优先级低的人的前面。

现在给出一个人员到来和银行办理业务的时间序列,请你在每次银行办理业务时输出客户的编号。

如果同时有多位优先级相同且最高的客户,则按照先来后到的顺序办理。


输入描述

输入第一行是一个正整数 n,表示输入的序列中的事件数量。(1 ≤ n ≤ 500)

接下来有 n 行,每行第一个字符为 ap

  • 当字符为 a 时,后面会有两个正整数 numx,表示到来的客户编号为 num,优先级为 x
  • 当字符为 p 时,表示当前优先级最高的客户去办理业务。

输出描述

输出包含若干行,对于每个 p,输出一行,仅包含一个正整数 num,表示办理业务的客户编号。


用例

输入

4
a 1 3
a 2 2
a 3 2
p

输出

1

题目解析

本题属于简单的优先队列应用,但需要注意以下几点:

  1. 优先级处理逻辑

    • 客户的优先级为 x,数字越小优先级越高。
    • 如果优先级相同时,按照先来后到的顺序处理。
  2. 队列管理

    • 在输入中记录每个客户的优先级和到来的顺序。
    • 使用优先队列(或排序结构)动态维护当前的最高优先级客户。
  3. 特殊情况处理

    • p 的数量可能多于 a 的数量。如果此时队列中没有客户办理业务,应当输出空。

解题思路

  1. 使用 Python 的 heapq 模块实现优先队列(小根堆)。
  2. 在插入客户时,将优先级和到达顺序一起存入堆中,以便动态管理优先级和到达顺序。
  3. p 操作时,提取队列中优先级最高且到来时间最早的客户。
  4. 维护一个备用的字典(或集合)以检查某些客户是否已经办理业务(防止重复处理)。

示例代码

python 复制代码
import heapq

def handle_bank_events(events):
    # 优先队列(小根堆),存储的是 (-优先级, 到达顺序, 客户编号)
    queue = []
    result = []
    count = 0  # 到达顺序计数器

    for event in events:
        if event[0] == 'a':
            _, num, priority = event
            # 插入堆中,优先级取反以便实现大优先级在堆顶
            heapq.heappush(queue, (-priority, count, num))
            count += 1
        elif event[0] == 'p':
            if queue:
                # 取出堆顶元素,即优先级最高且到达时间最早的客户
                _, _, customer_num = heapq.heappop(queue)
                result.append(customer_num)
            else:
                # 如果队列为空,输出空字符串(或跳过)
                result.append("")

    return result


# 输入处理
n = int(input())
events = []
for _ in range(n):
    line = input().split()
    if line[0] == 'a':
        events.append(('a', int(line[1]), int(line[2])))
    elif line[0] == 'p':
        events.append(('p',))

# 获取结果并输出
output = handle_bank_events(events)
for res in output:
    if res != "":
        print(res)

关键点说明

  1. 优先队列的设计

    • 使用 (-优先级, 到达顺序, 客户编号) 的元组存储到堆中,确保按照优先级从高到低排序。如果优先级相同,则按照到达顺序处理。
  2. 边界情况处理

    • 如果 p 操作时队列为空,优雅地处理并输出空字符串或跳过。
  3. 复杂度分析

    • 插入和删除操作的时间复杂度均为 O(log n),在最多 500 次操作内性能良好。

测试用例

用例 1

输入
4
a 1 3
a 2 2
a 3 2
p
输出
1

用例 2

输入
5
a 1 5
a 2 3
p
a 3 2
p
输出
2
3

用例 3

输入
3
p
a 1 3
p
输出
1

二、JavaScript算法源码

以下是 JavaScript 代码的中文详细注释和逻辑讲解:


代码逻辑

javascript 复制代码
/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");

// 创建 readline 接口,用于从控制台读取输入
const rl = readline.createInterface({
  input: process.stdin,  // 输入流
  output: process.stdout, // 输出流
});

const lines = [];  // 存储输入的行
let n;  // 存储输入的行数

// 监听输入事件
rl.on("line", (line) => {
  lines.push(line);  // 将输入的行存入 lines 数组

  if (lines.length === 1) {
    n = lines[0] - 0;  // 第一行是行数 n,转换为数字
  }

  // 当输入的行数达到 n + 1 时(第一行是 n,后面是 n 行数据)
  if (n && lines.length === n + 1) {
    lines.shift();  // 移除第一行(n)
    const seq = lines.map((line) => line.split(" "));  // 将每行按空格分割成数组
    getResult(n, seq);  // 调用算法函数
    lines.length = 0;  // 清空 lines 数组,准备下一轮输入
  }
});

// 算法入口
function getResult(n, seq) {
  // 创建优先队列,自定义比较函数
  const pq = new PriorityQueue((a, b) =>
    a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]
  );

  // 遍历每行输入
  for (let i = 0; i < n; i++) {
    const tmp = seq[i];  // 当前行的指令和数据

    switch (tmp[0]) {
      case "a":  // 如果是 "a" 指令
        const num = Number(tmp[1]);  // 获取第二个参数(数字)
        const x = Number(tmp[2]);  // 获取第三个参数(优先级)
        pq.offer([x, i, num]);  // 将 [x, i, num] 加入优先队列
        break;
      case "p":  // 如果是 "p" 指令
        const cust = pq.poll();  // 从优先队列中取出优先级最高的元素
        if (cust) console.log(cust[2]);  // 如果存在,输出第三个参数(数字)
        else console.log("");  // 如果队列为空,输出空字符串
    }
  }
}

// 基于堆实现优先队列
class PriorityQueue {
  constructor(cpr) {
    this.queue = [];  // 存储队列元素的数组
    this.cpr = cpr;  // 自定义比较函数
  }

  // 交换两个元素
  swap(a, b) {
    const tmp = this.queue[a];
    this.queue[a] = this.queue[b];
    this.queue[b] = tmp;
  }

  // 上浮操作(插入元素后调整堆)
  swim() {
    let c = this.queue.length - 1;  // 当前节点的索引

    while (c >= 1) {
      const f = Math.floor((c - 1) / 2);  // 父节点的索引

      // 如果当前节点比父节点优先级高,交换位置
      if (this.cpr(this.queue[c], this.queue[f]) < 0) {
        this.swap(c, f);
        c = f;  // 继续向上比较
      } else {
        break;  // 否则退出循环
      }
    }
  }

  // 入队操作
  offer(val) {
    this.queue.push(val);  // 将元素加入队列
    this.swim();  // 调整堆
  }

  // 下沉操作(删除元素后调整堆)
  sink() {
    let f = 0;  // 当前节点的索引

    while (true) {
      let c1 = 2 * f + 1;  // 左子节点的索引
      let c2 = c1 + 1;  // 右子节点的索引

      let c;  // 优先级更高的子节点的索引
      let val1 = this.queue[c1];  // 左子节点的值
      let val2 = this.queue[c2];  // 右子节点的值

      // 选择优先级更高的子节点
      if (val1 && val2) {
        c = this.cpr(val1, val2) < 0 ? c1 : c2;
      } else if (val1 && !val2) {
        c = c1;
      } else if (!val1 && val2) {
        c = c2;
      } else {
        break;  // 如果没有子节点,退出循环
      }

      // 如果子节点比当前节点优先级高,交换位置
      if (this.cpr(this.queue[c], this.queue[f]) < 0) {
        this.swap(c, f);
        f = c;  // 继续向下比较
      } else {
        break;  // 否则退出循环
      }
    }
  }

  // 出队操作
  poll() {
    this.swap(0, this.queue.length - 1);  // 交换堆顶和最后一个元素
    const res = this.queue.pop();  // 移除最后一个元素(原堆顶)
    this.sink();  // 调整堆
    return res;  // 返回移除的元素
  }

  // 查看堆顶元素
  peek() {
    return this.queue[0];
  }

  // 获取队列大小
  size() {
    return this.queue.length;
  }
}

代码讲解

  1. 输入处理

    • 使用 readline 模块从控制台读取输入。
    • 第一行是行数 n,后面是 n 行指令和数据。
    • 每行指令和数据按空格分割成数组,存储在 seq 中。
  2. 优先队列

    • 使用堆实现优先队列,支持插入(offer)和删除(poll)操作。
    • 自定义比较函数 cpr,用于比较元素的优先级。
  3. 指令处理

    • 如果是 "a" 指令,将 [x, i, num] 加入优先队列,其中 x 是优先级,i 是索引,num 是数字。
    • 如果是 "p" 指令,从优先队列中取出优先级最高的元素,并输出其 num 值。
  4. 堆操作

    • swim:插入元素后,从下往上调整堆。
    • sink:删除元素后,从上往下调整堆。
    • swap:交换两个元素的位置。
  5. 输出结果

    • 根据指令处理结果,输出相应的值。

示例解析

输入
5
a 10 1
a 20 2
p
a 30 3
p
运行结果
10
20
  • 解析:
    • 插入 [1, 0, 10][2, 1, 20]
    • 第一次 p 指令取出优先级最高的 [1, 0, 10],输出 10
    • 插入 [3, 2, 30]
    • 第二次 p 指令取出优先级最高的 [2, 1, 20],输出 20

总结

  • 该代码实现了一个基于堆的优先队列,支持插入和删除操作。
  • 通过自定义比较函数,可以灵活定义优先级规则。
  • 适用于需要动态维护优先级顺序的场景。

如果有其他问题,欢迎随时提问!

三、Java算法源码

以下是 Java 代码的中文详细注释和逻辑讲解:


代码逻辑

java 复制代码
import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);  // 创建 Scanner 对象,用于读取输入

    int n = Integer.parseInt(sc.nextLine());  // 读取第一行输入,表示操作的数量

    String[][] arr = new String[n][];  // 创建一个二维数组,用于存储每行的操作和参数
    for (int i = 0; i < n; i++) {
      arr[i] = sc.nextLine().split(" ");  // 将每行输入按空格分割成数组
    }

    getResult(n, arr);  // 调用算法函数
  }

  public static void getResult(int n, String[][] arr) {
    // 创建优先队列,自定义比较器
    PriorityQueue<int[]> pq =
        new PriorityQueue<>((a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);

    // 遍历每行操作
    for (int i = 0; i < n; i++) {
      String[] tmp = arr[i];  // 获取当前行的操作和参数

      switch (tmp[0]) {  // 根据操作类型进行处理
        case "a":  // 如果是 "a" 操作
          int num = Integer.parseInt(tmp[1]);  // 获取第二个参数(数字)
          int x = Integer.parseInt(tmp[2]);  // 获取第三个参数(优先级)
          pq.offer(new int[] {x, i, num});  // 将 [x, i, num] 加入优先队列
          break;
        case "p":  // 如果是 "p" 操作
          int[] poll = pq.poll();  // 从优先队列中取出优先级最高的元素
          if (poll != null) System.out.println(poll[2]);  // 如果存在,输出第三个参数(数字)
          else System.out.println("");  // 如果队列为空,输出空字符串
      }
    }
  }
}

代码讲解

  1. 输入处理

    • 使用 Scanner 从控制台读取输入。
    • 第一行是操作的数量 n
    • 接下来的 n 行是具体的操作和参数,每行按空格分割成数组,存储在二维数组 arr 中。
  2. 优先队列

    • 使用 PriorityQueue 实现优先队列。
    • 自定义比较器 (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]
      • 首先比较优先级 a[0]b[0],优先级小的排在前面。
      • 如果优先级相同,则比较插入顺序 a[1]b[1],先插入的排在前面。
  3. 操作处理

    • 如果是 "a" 操作:
      • [x, i, num] 加入优先队列,其中 x 是优先级,i 是插入顺序,num 是数字。
    • 如果是 "p" 操作:
      • 从优先队列中取出优先级最高的元素,并输出其 num 值。
      • 如果队列为空,输出空字符串。
  4. 输出结果

    • 根据操作类型处理结果,输出相应的值。

示例解析

输入
5
a 10 1
a 20 2
p
a 30 3
p
运行结果
10
20
  • 解析:
    • 插入 [1, 0, 10][2, 1, 20]
    • 第一次 p 操作取出优先级最高的 [1, 0, 10],输出 10
    • 插入 [3, 2, 30]
    • 第二次 p 操作取出优先级最高的 [2, 1, 20],输出 20

总结

  • 该代码实现了一个基于优先队列的操作处理系统。
  • 通过自定义比较器,可以灵活定义优先级规则。
  • 适用于需要动态维护优先级顺序的场景。

如果有其他问题,欢迎随时提问!

四、Python算法源码

以下是 Python 代码的中文详细注释和逻辑讲解:


代码逻辑

python 复制代码
import queue  # 导入 queue 模块,用于实现优先队列

# 输入获取
n = int(input())  # 读取第一行输入,表示操作的数量
seq = [input().split() for _ in range(n)]  # 读取接下来的 n 行输入,每行按空格分割成列表

# 定义一个客户类,实现自定义优先级
class Customer:
    def __init__(self, num, x, index):
        """
        :param num: 客户编号
        :param x: 客户优先级
        :param index: 客户先来后到顺序
        """
        self.num = num  # 客户编号
        self.x = x  # 客户优先级
        self.index = index  # 客户插入顺序

    def __lt__(self, other):
        """
        自定义比较方法,用于优先队列的排序
        :param other: 另一个 Customer 对象
        :return: 当前对象是否优先级更高
        """
        if self.x != other.x:
            return self.x < other.x  # 优先级小的排在前面
        else:
            return self.index < other.index  # 如果优先级相同,先插入的排在前面

# 算法入口
def getResult(n, seq):
    pq = queue.PriorityQueue()  # 创建一个优先队列

    for i in range(n):  # 遍历每行操作
        tmp = seq[i]  # 获取当前行的操作和参数

        if tmp[0] == 'a':  # 如果是 "a" 操作
            num = int(tmp[1])  # 获取第二个参数(客户编号)
            x = int(tmp[2])  # 获取第三个参数(优先级)
            pq.put(Customer(num, x, i))  # 将 Customer 对象加入优先队列
        elif tmp[0] == 'p':  # 如果是 "p" 操作
            if pq.qsize() > 0:  # 如果队列不为空
                customer = pq.get()  # 取出优先级最高的 Customer 对象
                print(customer.num)  # 输出客户编号
            else:
                print("")  # 如果队列为空,输出空字符串

# 算法调用
getResult(n, seq)  # 调用算法函数

代码讲解

  1. 输入处理

    • 使用 input() 读取输入。
    • 第一行是操作的数量 n
    • 接下来的 n 行是具体的操作和参数,每行按空格分割成列表,存储在 seq 中。
  2. 优先队列

    • 使用 queue.PriorityQueue 实现优先队列。
    • 自定义 Customer 类,实现 __lt__ 方法,用于定义优先级规则:
      • 首先比较优先级 x,优先级小的排在前面。
      • 如果优先级相同,则比较插入顺序 index,先插入的排在前面。
  3. 操作处理

    • 如果是 "a" 操作:
      • Customer(num, x, i) 加入优先队列,其中 num 是客户编号,x 是优先级,i 是插入顺序。
    • 如果是 "p" 操作:
      • 从优先队列中取出优先级最高的 Customer 对象,并输出其 num 值。
      • 如果队列为空,输出空字符串。
  4. 输出结果

    • 根据操作类型处理结果,输出相应的值。

示例解析

输入
5
a 10 1
a 20 2
p
a 30 3
p
运行结果
10
20
  • 解析:
    • 插入 Customer(10, 1, 0)Customer(20, 2, 1)
    • 第一次 p 操作取出优先级最高的 Customer(10, 1, 0),输出 10
    • 插入 Customer(30, 3, 2)
    • 第二次 p 操作取出优先级最高的 Customer(20, 2, 1),输出 20

总结

  • 该代码实现了一个基于优先队列的操作处理系统。
  • 通过自定义 Customer 类和 __lt__ 方法,可以灵活定义优先级规则。
  • 适用于需要动态维护优先级顺序的场景。

如果有其他问题,欢迎随时提问!

五、C/C++算法源码:

以下是 C++ 代码的中文详细注释和逻辑讲解:


代码逻辑

cpp 复制代码
#include <iostream>
#include <queue>
#include <vector>
#include <string>

using namespace std;

// 定义一个客户类,实现自定义优先级
class Customer {
public:
    int num;  // 客户编号
    int x;    // 客户优先级
    int index; // 客户先来后到顺序

    // 构造函数
    Customer(int num, int x, int index) : num(num), x(x), index(index) {}

    // 重载 < 运算符,用于优先队列的比较
    bool operator<(const Customer& other) const {
        if (x != other.x) {
            return x > other.x;  // 优先级小的排在前面
        } else {
            return index > other.index;  // 先来后到顺序小的排在前面
        }
    }
};

// 算法入口
void getResult(int n, vector<vector<string>>& seq) {
    priority_queue<Customer> pq;  // 创建一个优先队列

    for (int i = 0; i < n; i++) {  // 遍历每行操作
        vector<string> tmp = seq[i];  // 获取当前行的操作和参数

        if (tmp[0] == "a") {  // 如果是 "a" 操作
            int num = stoi(tmp[1]);  // 获取第二个参数(客户编号)
            int x = stoi(tmp[2]);  // 获取第三个参数(优先级)
            pq.push(Customer(num, x, i));  // 将 Customer 对象加入优先队列
        } else if (tmp[0] == "p") {  // 如果是 "p" 操作
            if (!pq.empty()) {  // 如果队列不为空
                Customer customer = pq.top();  // 取出优先级最高的 Customer 对象
                pq.pop();  // 从队列中移除该对象
                cout << customer.num << endl;  // 输出客户编号
            } else {
                cout << "" << endl;  // 如果队列为空,输出空字符串
            }
        }
    }
}

int main() {
    int n;
    cin >> n;  // 读取第一行输入,表示操作的数量
    cin.ignore();  // 忽略换行符

    vector<vector<string>> seq(n);  // 创建一个二维向量,用于存储每行的操作和参数
    for (int i = 0; i < n; i++) {  // 读取接下来的 n 行输入
        string line;
        getline(cin, line);  // 读取整行输入
        size_t pos = 0;
        string token;
        while ((pos = line.find(' ')) != string::npos) {  // 按空格分割字符串
            token = line.substr(0, pos);
            seq[i].push_back(token);  // 将分割后的字符串加入当前行的向量
            line.erase(0, pos + 1);
        }
        seq[i].push_back(line);  // 将最后一个字符串加入当前行的向量
    }

    // 调用算法
    getResult(n, seq);

    return 0;
}

代码讲解

  1. 输入处理

    • 使用 cin 读取输入。
    • 第一行是操作的数量 n
    • 接下来的 n 行是具体的操作和参数,每行按空格分割成字符串,存储在二维向量 seq 中。
  2. 优先队列

    • 使用 priority_queue 实现优先队列。
    • 自定义 Customer 类,重载 < 运算符,用于定义优先级规则:
      • 首先比较优先级 x,优先级小的排在前面。
      • 如果优先级相同,则比较插入顺序 index,先插入的排在前面。
  3. 操作处理

    • 如果是 "a" 操作:
      • Customer(num, x, i) 加入优先队列,其中 num 是客户编号,x 是优先级,i 是插入顺序。
    • 如果是 "p" 操作:
      • 从优先队列中取出优先级最高的 Customer 对象,并输出其 num 值。
      • 如果队列为空,输出空字符串。
  4. 输出结果

    • 根据操作类型处理结果,输出相应的值。

示例解析

输入
5
a 10 1
a 20 2
p
a 30 3
p
运行结果
10
20
  • 解析:
    • 插入 Customer(10, 1, 0)Customer(20, 2, 1)
    • 第一次 p 操作取出优先级最高的 Customer(10, 1, 0),输出 10
    • 插入 Customer(30, 3, 2)
    • 第二次 p 操作取出优先级最高的 Customer(20, 2, 1),输出 20

总结

  • 该代码实现了一个基于优先队列的操作处理系统。
  • 通过自定义 Customer 类和重载 < 运算符,可以灵活定义优先级规则。
  • 适用于需要动态维护优先级顺序的场景。

如果有其他问题,欢迎随时提问!

相关推荐
吾名招财1 小时前
open3d+opencv实现矩形框裁剪点云操作(C++)
c++·opencv·open3d·点云裁剪
兩尛1 小时前
项目概述、开发环境搭建(day01)
java·spring boot·web
闲人编程1 小时前
PID控制器 (Proportional-Integral-Derivative Controller) 算法详解及案例分析
python·算法·pid·路径规划·微分控制·积分控制·比例控制
诚丞成1 小时前
字符串算法篇——字里乾坤,算法织梦,解构字符串的艺术(下)
c++·算法
我想学LINUX2 小时前
【2024年华为OD机试】(C卷,100分)- 攀登者1 (Java & JS & Python&C/C++)
java·c语言·javascript·c++·python·游戏·华为od
Ring__Rain4 小时前
野指针bug
c++·bug
日暮温柔5 小时前
实现nacos配置修改后无需重启服务--使用@RefreshScope注解
java
武昌库里写JAVA5 小时前
React方向:react中5种Dom的操作方式
java·开发语言·spring boot·学习·课程设计
ThetaarSofVenice6 小时前
一个个顺序挨着来 - 责任链模式(Chain of Responsibility Pattern)
java·设计模式·责任链模式
xqhoj6 小时前
C++学习指南(七)——stack/queue/priority_queue
开发语言·c++