华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
一、题目描述
当小区通信设备上报警时,系统会自动生成待处理的工单,华为工单调度系统需要根据不同的策略,调度外线工程师(FME)上站修复工单对应的问题。 根据与运营商签订的合同,不同严重程度的工单被处理的时长要求不同,这个要求被称为我们称之为SLA时间。 假设华为和运营商商签有了运维合同,需要了每有一个外线工程师(FMEQ),每个工单根据问题严重度会给一个评分,如果SLA时间内完成修复的工单,华为获得工单评分奖励,超时SLA完成的工单不获得分,但必须完成该工单。运营商终会根据评分对华为进行结算。 请设计一种调度策略,根据该种调度策略完成所有工单,让外线工程师处理的工单获得的总分最多。 工程师调度可以顺序处理工单,当调度量为N,不会产生新的工单,每个工单处理修复耗时为1小时,请设计你的调度策略,完成业务目标。 不考虑外线工程师在小区之间行驶时。
二、输入描述
第一行为一个整数N,表示工单的数量。接下来N行,每行包括两个整数。第一个整数表示工单的SLA时间(小时),第二个数表示该工单的积分。
三、输出描述
输出一个整数表示可以获得的最大积分。
备注
- 工单数量 N ≤ 10^6
- SLA时间 ≤ 7 * 10^5
- 答案的最大积分不可以超过2147483647
四、测试用例
测试用例1:
1、输入
7
1 6
1 7
3 2
3 1
2 4
2 5
6 1
2、输出
15
3、说明
最多可获得15积分,其中一个调度结果完成工单顺序为2,6,3,1,7,5,4
测试用例2:
1、输入
5
2 10
1 5
2 7
1 8
3 6
2、输出
24
3、说明
工单排序后:(2,10), (1,8), (2,7), (3,6), (1,5)
分配结果:
工单(2,10)分配到时间槽2
工单(1,8)分配到时间槽1
工单(2,7)无法分配
工单(3,6)分配到时间槽3
工单(1,5)无法分配
总积分:10 + 8 + 6 = 24
五、解题思路
1、数据结构与算法
(1)贪心算法
选择高积分优先:通过将工单按照积分从高到低排序,优先分配高积分的工单,以确保总积分最大化。
选择最新可用时间槽:将每个工单分配到其SLA时间内的最新可用时间槽,尽可能为未来的工单留出更多空间。
(2)并查集(Union-Find)数据结构
高效查找和合并:并查集提供了接近常数时间复杂度的find和union操作,适用于大规模数据(N ≤ 10^6)的工单调度。
路径压缩优化:通过路径压缩技术,显著提高了并查集的操作效率,确保程序在处理大量工单时依然高效。
2、具体步骤:
- 输入读取:
- 使用Scanner读取工单数量N。
- 读取每个工单的deadline和score,同时记录最大deadline以初始化并查集。
- 工单排序:
- 按照积分从高到低排序,确保优先处理高积分的工单。
- 工单调度:
- 初始化并查集uf,大小为最大deadline。
- 遍历每个工单,尝试将其分配到最新的可用时间槽。如果分配成功,累加积分,并更新并查集。
- 输出结果:
- 输出所有分配成功的工单的总积分。
3、时间和空间复杂度
(1)时间复杂度:
排序工单的时间复杂度为O(N log N)。
并查集的find和union操作的平均时间复杂度接近O(1)。
因此,总体时间复杂度为O(N log N)。
(2)空间复杂度:
需要O(N)的空间存储工单信息。
并查集需要O(maxDeadline)的空间,最大为7 * 10^5。
六、Python算法源码
# 导入所需的模块
import sys
class UnionFind:
"""
并查集(Union-Find)数据结构,用于高效地查找和合并可用的时间槽
"""
def __init__(self, size):
# 初始化父节点数组,每个节点的父节点为自己
self.parent = list(range(size + 1))
def find(self, x):
"""
查找当前节点x的根节点,并进行路径压缩
"""
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # 路径压缩
return self.parent[x]
def union(self, x, y):
"""
合并两个节点x和y的集合
"""
root_x = self.find(x)
root_y = self.find(y)
if root_x != root_y:
self.parent[root_x] = root_y # 将x的根节点指向y的根节点
class Job:
"""
工单类,包含SLA时间和积分
"""
def __init__(self, deadline, score):
self.deadline = deadline # 工单的SLA时间
self.score = score # 工单的积分
def construct_longest_palindrome(s):
pass # 占位,不需要此函数
def main():
# 读取所有输入
input = sys.stdin.read().split()
idx = 0
N = int(input[idx]) # 工单数量
idx += 1
jobs = []
max_deadline = 0 # 记录最大SLA时间
# 读取每个工单的SLA时间和积分
for _ in range(N):
deadline = int(input[idx])
score = int(input[idx + 1])
jobs.append(Job(deadline, score))
if deadline > max_deadline:
max_deadline = deadline
idx += 2
# 按照积分从高到低排序,如果积分相同,则按SLA时间从小到大排序
jobs.sort(key=lambda job: (-job.score, job.deadline))
# 初始化并查集,大小为最大SLA时间
uf = UnionFind(max_deadline)
total_score = 0 # 使用long避免溢出(Python的int没有限制)
# 遍历每个工单,尝试分配到最新的可用时间槽
for job in jobs:
# 查找当前工单的最新可用时间槽
available_slot = uf.find(job.deadline)
if available_slot > 0:
total_score += job.score # 累加积分
# 将当前时间槽与前一个时间槽合并,表示该时间槽已被占用
uf.union(available_slot, available_slot - 1)
# 输出最大积分
print(total_score)
if __name__ == "__main__":
main()
七、JavaScript算法源码
// 最长回文串构造程序
/**
* 工单类,包含SLA时间和积分
*/
class Job {
constructor(deadline, score) {
this.deadline = deadline; // 工单的SLA时间
this.score = score; // 工单的积分
}
}
/**
* 并查集(Union-Find)数据结构,用于高效地查找和合并可用的时间槽
*/
class UnionFind {
constructor(size) {
// 初始化父节点数组,每个节点的父节点为自己
this.parent = Array.from({length: size + 1}, (_, i) => i);
}
/**
* 查找当前节点x的根节点,并进行路径压缩
* @param {number} x 当前节点
* @returns {number} 根节点
*/
find(x) {
if (this.parent[x] !== x) {
this.parent[x] = this.find(this.parent[x]); // 路径压缩
}
return this.parent[x];
}
/**
* 合并两个节点x和y的集合
* @param {number} x 节点x
* @param {number} y 节点y
*/
union(x, y) {
let rootX = this.find(x);
let rootY = this.find(y);
if (rootX !== rootY) {
this.parent[rootX] = rootY; // 将x的根节点指向y的根节点
}
}
}
/**
* 主函数,读取输入并计算最大积分
*/
function main() {
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on('line', function(line) {
input = line.trim().split(/\s+/).map(Number);
}).on('close', function() {
let idx = 0;
const N = input[idx++]; // 工单数量
let jobs = [];
let max_deadline = 0; // 记录最大SLA时间
// 读取每个工单的SLA时间和积分
for (let i = 0; i < N; i++) {
let deadline = input[idx++];
let score = input[idx++];
jobs.push(new Job(deadline, score));
if (deadline > max_deadline) {
max_deadline = deadline;
}
}
// 按照积分从高到低排序,如果积分相同,则按SLA时间从小到大排序
jobs.sort((a, b) => {
if (b.score !== a.score) {
return b.score - a.score; // 降序排列积分
} else {
return a.deadline - b.deadline; // 升序排列SLA时间
}
});
// 初始化并查集,大小为最大SLA时间
let uf = new UnionFind(max_deadline);
let total_score = 0; // 累加积分
// 遍历每个工单,尝试分配到最新的可用时间槽
for (let job of jobs) {
// 查找当前工单的最新可用时间槽
let available_slot = uf.find(job.deadline);
if (available_slot > 0) {
total_score += job.score; // 累加积分
// 将当前时间槽与前一个时间槽合并,表示该时间槽已被占用
uf.union(available_slot, available_slot - 1);
}
}
// 输出最大积分
console.log(total_score);
process.exit(0);
});
}
main();
八、C算法源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 工单类,包含SLA时间和积分
typedef struct {
int deadline; // 工单的SLA时间
int score; // 工单的积分
} Job;
// 并查集(Union-Find)数据结构,用于高效地查找和合并可用的时间槽
typedef struct {
int *parent;
} UnionFind;
/**
* 初始化并查集
* @param size 并查集的大小
* @return 初始化后的并查集
*/
UnionFind* init_union_find(int size) {
UnionFind* uf = (UnionFind*)malloc(sizeof(UnionFind));
uf->parent = (int*)malloc((size + 1) * sizeof(int));
for(int i = 0; i <= size; i++) {
uf->parent[i] = i; // 初始化每个节点的父节点为自己
}
return uf;
}
/**
* 查找当前节点x的根节点,并进行路径压缩
* @param uf 并查集
* @param x 当前节点
* @return 根节点
*/
int find(UnionFind* uf, int x) {
if(uf->parent[x] != x) {
uf->parent[x] = find(uf, uf->parent[x]); // 路径压缩
}
return uf->parent[x];
}
/**
* 合并两个节点x和y的集合
* @param uf 并查集
* @param x 节点x
* @param y 节点y
*/
void union_sets(UnionFind* uf, int x, int y) {
int root_x = find(uf, x);
int root_y = find(uf, y);
if(root_x != root_y) {
uf->parent[root_x] = root_y; // 将x的根节点指向y的根节点
}
}
/**
* 比较函数,用于qsort,按照积分从高到低排序,如果积分相同,则按SLA时间从小到大排序
*/
int compare(const void* a, const void* b) {
Job* jobA = (Job*)a;
Job* jobB = (Job*)b;
if(jobB->score != jobA->score) {
return jobB->score - jobA->score; // 降序排列积分
} else {
return jobA->deadline - jobB->deadline; // 升序排列SLA时间
}
}
int main() {
int N;
// 读取工单数量
if(scanf("%d", &N) != 1) {
return 0;
}
Job* jobs = (Job*)malloc(N * sizeof(Job));
int max_deadline = 0; // 记录最大SLA时间
// 读取每个工单的SLA时间和积分
for(int i = 0; i < N; i++) {
if(scanf("%d %d", &jobs[i].deadline, &jobs[i].score) != 2) {
return 0;
}
if(jobs[i].deadline > max_deadline) {
max_deadline = jobs[i].deadline;
}
}
// 按照积分从高到低排序
qsort(jobs, N, sizeof(Job), compare);
// 初始化并查集,大小为最大SLA时间
UnionFind* uf = init_union_find(max_deadline);
long long total_score = 0; // 使用long long避免溢出
// 遍历每个工单,尝试分配到最新的可用时间槽
for(int i = 0; i < N; i++) {
int available_slot = find(uf, jobs[i].deadline);
if(available_slot > 0) {
total_score += jobs[i].score; // 累加积分
// 将当前时间槽与前一个时间槽合并,表示该时间槽已被占用
union_sets(uf, available_slot, available_slot - 1);
}
}
// 输出最大积分
printf("%lld\n", total_score);
// 释放内存
free(jobs);
free(uf->parent);
free(uf);
return 0;
}
九、C++算法源码
#include <bits/stdc++.h>
using namespace std;
/**
* 工单类,包含SLA时间和积分
*/
struct Job {
int deadline; // 工单的SLA时间(小时)
int score; // 工单的积分
};
/**
* 并查集(Union-Find)数据结构,用于高效地查找和合并可用的时间槽
*/
struct UnionFind {
vector<int> parent; // 父节点数组
/**
* 构造函数,初始化并查集
* @param size 并查集的大小(最大SLA时间)
*/
UnionFind(int size) {
parent.resize(size + 1);
for(int i = 0; i <= size; i++) {
parent[i] = i; // 初始化每个节点的父节点为自己
}
}
/**
* 查找当前节点x的根节点,并进行路径压缩
* @param x 当前节点
* @return x的根节点
*/
int find_set(int x) {
if(parent[x] != x) {
parent[x] = find_set(parent[x]); // 路径压缩,递归查找并更新父节点
}
return parent[x];
}
/**
* 合并两个节点x和y的集合
* @param x 节点x
* @param y 节点y
*/
void union_set(int x, int y) {
int root_x = find_set(x); // 查找x的根节点
int root_y = find_set(y); // 查找y的根节点
if(root_x != root_y) {
parent[root_x] = root_y; // 将x的根节点指向y的根节点,实现合并
}
}
};
/**
* 比较函数,用于排序工单
* 按照积分从高到低排序,如果积分相同,则按SLA时间从小到大排序
* @param a 第一个工单
* @param b 第二个工单
* @return true 如果a应该排在b前面,否则false
*/
bool compare_jobs(const Job &a, const Job &b) {
if(a.score != b.score) {
return a.score > b.score; // 积分高的排在前面
} else {
return a.deadline < b.deadline; // SLA时间小的排在前面
}
}
int main(){
ios::sync_with_stdio(false); // 关闭C++的同步机制,加快输入输出速度
cin.tie(0); // 解除cin和cout的绑定,进一步加快速度
int N; // 工单数量
cin >> N; // 读取工单数量
vector<Job> jobs(N); // 创建一个大小为N的工单数组
int max_deadline = 0; // 记录最大SLA时间
// 读取每个工单的SLA时间和积分
for(int i = 0; i < N; i++) {
cin >> jobs[i].deadline >> jobs[i].score; // 读取SLA时间和积分
if(jobs[i].deadline > max_deadline) {
max_deadline = jobs[i].deadline; // 更新最大SLA时间
}
}
// 按照积分从高到低排序,如果积分相同,则按SLA时间从小到大排序
sort(jobs.begin(), jobs.end(), compare_jobs);
// 初始化并查集,大小为最大SLA时间
UnionFind uf(max_deadline);
long long total_score = 0; // 使用long long避免溢出,存储总积分
// 遍历每个工单,尝试分配到最新的可用时间槽
for(auto &job : jobs){
// 查找当前工单的最新可用时间槽
int available_slot = uf.find_set(job.deadline);
if(available_slot > 0){
total_score += job.score; // 累加积分
// 将当前时间槽与前一个时间槽合并,表示该时间槽已被占用
uf.union_set(available_slot, available_slot - 1);
}
}
// 输出最大积分
cout << total_score << "\n";
return 0;
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2024 E卷 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。