
专栏导读
本专栏收录于《华为OD机试真题(Java/Python/JS/C/C++/Go)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。
一、题目内容
已知存在两个任务队列 A A A、 B B B,队列成员表示单个任务耗时;为了缩短整体运行时间,需要队列 A A A 和队列 B B B 中任务的总耗时一致。
初始时队列 A A A 和队列 B B B 的任务总耗时不同,要求只进行一次任务交换:从 A A A 中选择一个任务,从 B B B 中选择一个任务,两者互换;交换后要求两个队列的任务总耗时相等。
请你找出队列 A A A 和队列 B B B 中需要交换的任务位置 [ i , j ] [i,j] [i,j](数组下标,从 0 开始),规则说明:
-
单个任务耗时取值 0 < T < 100 0 < T < 100 0<T<100。
-
用例保证均有解,且满足条件的下标对 [ i , j ] [i,j] [i,j] 唯一。
-
队列 A A A 和队列 B B B 长度为 1 ∼ 50 1 \sim 50 1∼50。
二、输入描述
整型数组 A A A,由队列 A A A 中的任务耗时构成的数组。
整型数组 B B B,由队列 B B B 中的任务耗时构成的数组。
三、输出描述
数组 [ i , j ] [i,j] [i,j],其中 i i i 是 A A A 中参与交换任务的原始下标, j j j 是 B B B 中参与交换任务的原始下标,下标从 0 开始。
四、测试用例
测试用例1:
1、输入
1,2,9
1,2,3,4
2、输出
1,0
3、说明
基础场景:A 总耗时大于 B
初始总耗时为 12 和 10。交换 A[1]=2 与 B[0]=1 后,两个队列的总耗时均变为 11。
测试用例2:
1、输入
1,15,9
2,4,6,8,25
2、输出
1,4
3、说明
反向场景:A 总耗时小于 B
初始总耗时为 25 和 45。交换 A[1]=15 与 B[4]=25 后,两个队列的总耗时均变为 35。验证负数差值的处理。
五、解题思路
解题思路:数学推导 + 定长数组映射
1、数学公式推导
首先遍历两个任务队列,计算总耗时 sumA 和 sumB。假设交换的任务分别为 A[i] 和 B[j],交换后两个队列总耗时相等,即:
sumA - A[i] + B[j] = sumB - B[j] + A[i]
整理得到:
A[i] - B[j] = (sumA - sumB) / 2
因此,当确定 A 中的任务时,可以直接计算 B 中需要匹配的任务耗时,而不必枚举所有交换组合。
2、数据结构选择
由于题目规定单个任务耗时在 1~99 之间,采用长度为 100 的整型数组 indexInB 建立映射。数组下标表示任务耗时,数组元素表示该任务在 B 中的原始下标。
初始化所有元素为 -1,表示对应耗时不存在。遍历 B,将每个任务的原始下标记录下来。
该结构比普通哈希表更简单,没有哈希冲突问题,且能够直接按耗时进行常数时间查找。
3、算法实现
遍历队列 A,对每个位置 i,计算目标耗时:
target = A[i] - (sumA - sumB) / 2
检查 target 是否在 1~99 范围内,以及 indexInB[target] 是否不为 -1。如果满足条件,则返回 [i, indexInB[target]]。
若两个队列总耗时之差为奇数,则无法通过交换整数任务达到平衡。题目保证存在唯一解,因此正常情况下总能找到结果。
4、正确性与复杂度
算法完全依据交换后总耗时相等的充要条件查找,因此找到的下标对一定满足要求。
相比 O(n×m) 的双重循环,数组映射避免了重复比较;相比排序算法,不会改变任务的原始下标。
- 时间复杂度:O(n+m+100),即 O(n+m)。
- 空间复杂度:O(100),即 O(1)。
- n、m 分别为两个任务队列的长度。
该算法实现简单、性能稳定,适合本题给定的数据范围。
六、Python算法源码
import sys
def parse_array(line):
# 输入格式为逗号分隔的整数,同时允许任务耗时两侧存在空格。
return [int(part.strip()) for part in line.strip().split(',')]
def find_swap(a, b):
sum_a, sum_b = sum(a), sum(b)
# 交换 a[i]、b[j] 后总耗时相等,可以推导出:
# a[i] - b[j] = (sum_a - sum_b) / 2。
diff = sum_a - sum_b
if diff % 2 != 0:
return -1, -1
half = diff // 2
# 耗时范围为 1~99,直接用下标存 B 中对应任务的原始位置。
# -1 代表没有这个任务耗时,因此无需对队列进行排序。
index_in_b = [-1] * 100
for j, value in enumerate(b):
index_in_b[value] = j
# 对 A 中每项算出 B 里需要的耗时,再 O(1) 查询其位置。
for i, value in enumerate(a):
target = value - half
if 1 <= target <= 99 and index_in_b[target] != -1:
return i, index_in_b[target]
return -1, -1 # 题目保证有唯一解。
def main():
a = parse_array(sys.stdin.readline())
b = parse_array(sys.stdin.readline())
i, j = find_swap(a, b)
print(f"{i},{j}")
if __name__ == "__main__":
main()
七、JavaScript算法源码
const fs = require('fs');
// 读取两行逗号分隔的输入,保留元素在原队列中的排列顺序。
const lines = fs.readFileSync(0, 'utf8').trim().split(/\r?\n/);
const parseArray = (line) =>
line.split(',').map((part) => Number(part.trim()));
function findSwap(a, b) {
const sumA = a.reduce((sum, x) => sum + x, 0);
const sumB = b.reduce((sum, x) => sum + x, 0);
// 由交换前后的和相等可得:
// a[i] - b[j] = (sumA - sumB) / 2。
const diff = sumA - sumB;
if (diff % 2 !== 0) {
return [-1, -1];
}
const half = diff / 2;
// 耗时取值是 1~99,直接以耗时作为索引,
// 记录其在 B 中的原始下标。
const indexInB = Array(100).fill(-1);
for (let j = 0; j < b.length; j++) {
indexInB[b[j]] = j;
}
// 逐一计算 A 中任务所需匹配的 B 中耗时。
// 找到后即可返回原始下标。
for (let i = 0; i < a.length; i++) {
const target = a[i] - half;
if (target >= 1 && target <= 99
&& indexInB[target] !== -1) {
return [i, indexInB[target]];
}
}
return [-1, -1]; // 题目保证有唯一解。
}
const a = parseArray(lines[0]);
const b = parseArray(lines[1]);
const [i, j] = findSwap(a, b);
console.log(`${i},${j}`);
八、C算法源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 将以逗号分隔的一行任务耗时写入数组,返回元素个数。
// strtok 修改传入的字符串,因此输入行使用可写字符数组。
int parseArray(char line[], int arr[]) {
int count = 0;
char *token = strtok(line, ",");
while (token != NULL) {
arr[count++] = atoi(token);
token = strtok(NULL, ",");
}
return count;
}
void findSwap(const int a[], int n, const int b[], int m,
int *resultI, int *resultJ) {
int sumA = 0, sumB = 0;
*resultI = -1;
*resultJ = -1;
for (int i = 0; i < n; i++) {
sumA += a[i];
}
for (int j = 0; j < m; j++) {
sumB += b[j];
}
// 交换后两个队列总耗时相等,等价于:
// a[i] - b[j] = (sumA - sumB) / 2。
int diff = sumA - sumB;
if (diff % 2 != 0) {
return;
}
int half = diff / 2;
// 耗时只可能为 1~99,建立:
// “任务耗时 -> B 中原始下标”的映射。
// 初始设置 -1,用于区分“下标为 0”与“不存在”。
int indexInB[100];
for (int t = 0; t < 100; t++) {
indexInB[t] = -1;
}
for (int j = 0; j < m; j++) {
indexInB[b[j]] = j;
}
// 遍历 A,O(1) 判断 B 是否存在目标耗时。
// 无须双层遍历。
for (int i = 0; i < n; i++) {
int target = a[i] - half;
if (target >= 1 && target <= 99
&& indexInB[target] != -1) {
*resultI = i;
*resultJ = indexInB[target];
return;
}
}
}
int main(void) {
char lineA[1024], lineB[1024];
int a[50], b[50];
if (fgets(lineA, sizeof(lineA), stdin) == NULL) {
return 0;
}
if (fgets(lineB, sizeof(lineB), stdin) == NULL) {
return 0;
}
int n = parseArray(lineA, a);
int m = parseArray(lineB, b);
int i, j;
findSwap(a, n, b, m, &i, &j);
printf("%d,%d\n", i, j);
return 0;
}
九、C++算法源码
#include <array>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
using namespace std;
vector<int> parseArray(const string &line) {
vector<int> arr;
string token;
stringstream ss(line);
// 以逗号分隔输入。
// stoi 可以正确处理数字前后的空格。
while (getline(ss, token, ',')) {
arr.push_back(stoi(token));
}
return arr;
}
pair<int, int> findSwap(const vector<int> &a,
const vector<int> &b) {
int sumA = 0, sumB = 0;
for (int x : a) {
sumA += x;
}
for (int y : b) {
sumB += y;
}
// 由 sumA-a[i]+b[j] == sumB-b[j]+a[i]
// 可推导出:
// a[i] - b[j] = (sumA - sumB) / 2。
int diff = sumA - sumB;
if (diff % 2 != 0) {
return {-1, -1};
}
int half = diff / 2;
// 因为任务耗时范围是 1~99,
// 采用固定大小数组保存 B 的原始下标。
// fill(-1) 表示尚无该耗时。
// 不对任务排序,以免丢失原始下标。
array<int, 100> indexInB;
indexInB.fill(-1);
for (int j = 0; j < (int)b.size(); j++) {
indexInB[b[j]] = j;
}
// 根据 A 的任务耗时直接计算所需的 B 中任务耗时。
// 使用数组进行 O(1) 查找。
for (int i = 0; i < (int)a.size(); i++) {
int target = a[i] - half;
if (target >= 1 && target <= 99
&& indexInB[target] != -1) {
return {i, indexInB[target]};
}
}
return {-1, -1}; // 题目保证有唯一解。
}
int main() {
string lineA, lineB;
getline(cin, lineA);
getline(cin, lineB);
vector<int> a = parseArray(lineA);
vector<int> b = parseArray(lineB);
auto answer = findSwap(a, b);
cout << answer.first << ","
<< answer.second << '\n';
return 0;
}
十、Go算法源码
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func parseArray(line string) []int {
parts := strings.Split(strings.TrimSpace(line), ",")
arr := make([]int, len(parts))
// 逐个解析逗号分隔的任务耗时。
// TrimSpace 可处理数字附近的空格。
for i, part := range parts {
arr[i], _ = strconv.Atoi(strings.TrimSpace(part))
}
return arr
}
func findSwap(a, b []int) (int, int) {
sumA, sumB := 0, 0
// 第一步:分别计算两个队列的总耗时。
for _, x := range a {
sumA += x
}
for _, y := range b {
sumB += y
}
// 第二步:通过数学公式计算耗时差的一半。
// 交换后总耗时相等,等价于:
// a[i] - b[j] = (sumA - sumB) / 2。
diff := sumA - sumB
if diff%2 != 0 {
return -1, -1
}
half := diff / 2
// 第三步:任务耗时仅为 1~99,
// 用固定数组存放 B 中对应任务的原始下标。
// 初始化为 -1,表示对应任务耗时不存在。
var indexInB [100]int
for i := range indexInB {
indexInB[i] = -1
}
for j, value := range b {
indexInB[value] = j
}
// 第四步:枚举 A 的每个任务,
// 直接查找 B 中对应耗时。
// 避免 O(n*m) 双重循环。
for i, value := range a {
target := value - half
if target >= 1 &&
target <= 99 &&
indexInB[target] != -1 {
return i, indexInB[target]
}
}
// 题目保证有唯一解。
return -1, -1
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
return
}
a := parseArray(scanner.Text())
if !scanner.Scan() {
return
}
b := parseArray(scanner.Text())
i, j := findSwap(a, b)
fmt.Printf("%d,%d\n", i, j)
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++/Go 新系统 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++/Go)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

本博客详解华为OD机试真题'均衡调度',核心是通过一次任务交换使两队列总耗时相等。基于数学推导得出交换条件:target = (sumA - sumB) / 2 + B[j],进而利用定长数组(长度100)实现O(1)耗时映射查找,避免哈希冲突。算法时间复杂度O(n+m),空间复杂度O(1),适用于任务耗时范围1~99的约束场景。
335

被折叠的 条评论
为什么被折叠?



